• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

apache / iotdb / #9917

24 Aug 2023 04:16PM UTC coverage: 47.861% (+0.009%) from 47.852%
#9917

push

travis_ci

web-flow
Feature/streamlined grafana plugin profile (#10907)

80245 of 167662 relevant lines covered (47.86%)

0.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.77
/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/AbstractTreeVisitor.java
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19

20
package org.apache.iotdb.commons.schema.tree;
21

22
import org.apache.iotdb.commons.conf.IoTDBConstant;
23
import org.apache.iotdb.commons.path.PartialPath;
24
import org.apache.iotdb.commons.path.PathPatternTree;
25
import org.apache.iotdb.commons.path.fa.IFAState;
26
import org.apache.iotdb.commons.path.fa.IFATransition;
27
import org.apache.iotdb.commons.path.fa.IPatternFA;
28
import org.apache.iotdb.commons.path.fa.dfa.PatternDFA;
29
import org.apache.iotdb.commons.path.fa.match.IStateMatchInfo;
30
import org.apache.iotdb.commons.path.fa.match.StateMultiMatchInfo;
31
import org.apache.iotdb.commons.path.fa.match.StateSingleMatchInfo;
32
import org.apache.iotdb.commons.schema.SchemaConstant;
33

34
import org.slf4j.Logger;
35
import org.slf4j.LoggerFactory;
36

37
import java.util.ArrayDeque;
38
import java.util.ArrayList;
39
import java.util.Deque;
40
import java.util.Iterator;
41
import java.util.List;
42
import java.util.Map;
43
import java.util.NoSuchElementException;
44

45
/**
46
 * This class defines a dfs-based algorithm of tree-traversing with path pattern match, and support
47
 * iterating each element of the result.
48
 *
49
 * <p>This class takes three basis parameters as input:
50
 *
51
 * <ol>
52
 *   <li>N root: the root node of the tree to be traversed.
53
 *   <li>PartialPath patPattern: the pattern of path that the path of target element matches
54
 *   <li>boolean isPrefixMatch: whether the pathPattern is used for matching the prefix; if so, all
55
 *       elements with path starting with the matched prefix will be collected
56
 * </ol>
57
 *
58
 * <p>If any tree wants to integrate and use this class. The following steps must be attained:
59
 *
60
 * <ol>
61
 *   <li>The node of the tree must implement ITreeNode interface and the generic N should be defined
62
 *       as the node class.
63
 *   <li>The result type R should be defined.
64
 *   <li>Implement the abstract methods, and for the concrete requirements, please refer to the
65
 *       javadoc of specific method.
66
 * </ol>
67
 *
68
 * @param <N> The node consisting the tree.
69
 * @param <R> The result extracted from the tree.
70
 */
71
public abstract class AbstractTreeVisitor<N extends ITreeNode, R> implements SchemaIterator<R> {
72
  private static final Logger logger = LoggerFactory.getLogger(AbstractTreeVisitor.class);
1✔
73

74
  // command parameters
75
  protected N root;
76

77
  // finite automation constructed from given path pattern or pattern tree
78
  protected final IPatternFA patternFA;
79
  // deterministic finite automation for filtering traversed subtrees
80
  private final PatternDFA scopeDFA;
81
  private final boolean allScope;
82

83
  // run time variables
84
  // stack to store children iterator of visited ancestor
85
  private final Deque<VisitorStackEntry> visitorStack = new ArrayDeque<>();
1✔
86
  // stack to store ancestor nodes and their FA state match info
87
  private final List<AncestorStackEntry> ancestorStack = new ArrayList<>();
1✔
88
  // the FA match process can traceback since this ancestor in ancestor stack
89
  // this field will be updated during iterating children in all subclass of
90
  // AbstractChildrenIterator
91
  private int firstAncestorOfTraceback = -1;
1✔
92
  // the FA state match info of current node
93
  // this field will be updated during iterating children in all subclass of
94
  // AbstractChildrenIterator
95
  private IStateMatchInfo currentStateMatchInfo;
96
  // whether to visit the subtree of current node
97
  private boolean shouldVisitSubtree;
98
  // record exception if failed
99
  private Throwable throwable;
100

101
  // cached result variables
102
  private N nextMatchedNode;
103

104
  // only used for wrapper
105
  protected AbstractTreeVisitor() {
1✔
106
    root = null;
1✔
107
    patternFA = null;
1✔
108
    scopeDFA = SchemaConstant.ALL_MATCH_DFA;
1✔
109
    allScope = true;
1✔
110
  }
1✔
111

112
  protected AbstractTreeVisitor(N root, PartialPath pathPattern, boolean isPrefixMatch) {
113
    this(root, pathPattern, isPrefixMatch, null);
1✔
114
  }
1✔
115

116
  protected AbstractTreeVisitor(
117
      N root, PartialPath pathPattern, boolean isPrefixMatch, PathPatternTree scope) {
1✔
118
    this.root = root;
1✔
119

120
    boolean usingDFA = false;
1✔
121
    // Use DFA if there are ** and no regex node in pathPattern
122
    for (String pathNode : pathPattern.getNodes()) {
1✔
123
      if (IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD.equals(pathNode)) {
1✔
124
        // ** node
125
        usingDFA = true;
1✔
126
      } else if (pathNode.length() > 1
1✔
127
          && pathNode.contains(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD)) {
1✔
128
        // regex node
129
        usingDFA = false;
1✔
130
        break;
1✔
131
      }
132
    }
133
    this.patternFA =
1✔
134
        usingDFA
1✔
135
            ? new IPatternFA.Builder().pattern(pathPattern).isPrefixMatch(isPrefixMatch).buildDFA()
1✔
136
            : new IPatternFA.Builder().pattern(pathPattern).isPrefixMatch(isPrefixMatch).buildNFA();
1✔
137
    this.scopeDFA =
1✔
138
        scope == null
1✔
139
            ? SchemaConstant.ALL_MATCH_DFA
1✔
140
            : (PatternDFA) new IPatternFA.Builder().patternTree(scope).buildDFA();
1✔
141
    this.allScope = this.scopeDFA == SchemaConstant.ALL_MATCH_DFA;
1✔
142
  }
1✔
143

144
  /** This method must be invoked before iteration */
145
  protected final void initStack() {
146
    IFAState initialState = patternFA.getInitialState();
1✔
147
    IFATransition transition =
1✔
148
        patternFA.getPreciseMatchTransition(initialState).get(root.getName());
1✔
149
    if (transition == null) {
1✔
150
      // the visitor stack will be empty and the result of hasNext() will be false
151
      return;
×
152
    }
153
    IFAState rootState = patternFA.getNextState(initialState, transition);
1✔
154
    IFAState initScopeState = scopeDFA.getNextState(scopeDFA.getInitialState(), root.getName());
1✔
155
    currentStateMatchInfo = new StateSingleMatchInfo(patternFA, rootState, initScopeState);
1✔
156
    visitorStack.push(new VisitorStackEntry(createChildrenIterator(root), 1));
1✔
157
    ancestorStack.add(new AncestorStackEntry(root, currentStateMatchInfo));
1✔
158
  }
1✔
159

160
  public void reset() {
161
    close();
1✔
162
    visitorStack.clear();
1✔
163
    ancestorStack.clear();
1✔
164
    nextMatchedNode = null;
1✔
165
    firstAncestorOfTraceback = -1;
1✔
166
    initStack();
1✔
167
  }
1✔
168

169
  @Override
170
  public void close() {
171
    if (nextMatchedNode != null && !shouldVisitSubtree) {
1✔
172
      // release nextMatchedNode
173
      releaseNode(nextMatchedNode);
×
174
    }
175
    while (!visitorStack.isEmpty()) {
1✔
176
      popStack();
1✔
177
    }
178
  }
1✔
179

180
  @Override
181
  public boolean hasNext() {
182
    if (throwable == null && nextMatchedNode == null) {
1✔
183
      try {
184
        getNext();
1✔
185
      } catch (Throwable e) {
×
186
        logger.warn(e.getMessage(), e);
×
187
        setFailure(e);
×
188
      }
1✔
189
    }
190
    return throwable == null && nextMatchedNode != null;
1✔
191
  }
192

193
  @Override
194
  public R next() {
195
    if (!hasNext()) {
1✔
196
      throw new NoSuchElementException();
×
197
    }
198
    R result = generateResult(nextMatchedNode);
1✔
199
    if (!shouldVisitSubtree) {
1✔
200
      // release nextMatchedNode
201
      releaseNode(nextMatchedNode);
1✔
202
    }
203
    nextMatchedNode = null;
1✔
204
    return result;
1✔
205
  }
206

207
  private void getNext() {
208
    nextMatchedNode = null;
1✔
209
    VisitorStackEntry stackEntry;
210
    AbstractChildrenIterator iterator;
211
    while (!visitorStack.isEmpty()) {
1✔
212
      stackEntry = visitorStack.peek();
1✔
213
      iterator = stackEntry.iterator;
1✔
214

215
      if (!iterator.hasNext()) {
1✔
216
        popStack();
1✔
217
        continue;
1✔
218
      }
219

220
      N nextTempNode = iterator.next();
1✔
221

222
      shouldVisitSubtree = false;
1✔
223
      if (currentStateMatchInfo.hasFinalState()) {
1✔
224
        if (acceptFullMatchedNode(nextTempNode)) {
1✔
225
          nextMatchedNode = nextTempNode;
1✔
226
        }
227
        shouldVisitSubtree = shouldVisitSubtreeOfFullMatchedNode(nextTempNode);
1✔
228
      } else {
229
        if (acceptInternalMatchedNode(nextTempNode)) {
1✔
230
          nextMatchedNode = nextTempNode;
1✔
231
        }
232
        shouldVisitSubtree = shouldVisitSubtreeOfInternalMatchedNode(nextTempNode);
1✔
233
      }
234

235
      if (shouldVisitSubtree) {
1✔
236
        pushChildren(nextTempNode);
1✔
237
        // After adding nextTempNode into ancestorStack, nextTempNode will be released finally.
238
      } else if (nextMatchedNode != nextTempNode) {
1✔
239
        // Else if nextTempNode is not accepted, it needs to be released.
240
        releaseNode(nextTempNode);
1✔
241
      }
242
      // Otherwise, it will be released when invoking next()
243

244
      if (nextMatchedNode != null) {
1✔
245
        return;
1✔
246
      }
247
    }
1✔
248
  }
1✔
249

250
  private void pushChildren(N parent) {
251
    visitorStack.push(
1✔
252
        new VisitorStackEntry(
253
            createChildrenIterator(parent),
1✔
254
            visitorStack.isEmpty() ? 1 : visitorStack.peek().level + 1));
1✔
255
    ancestorStack.add(new AncestorStackEntry(parent, currentStateMatchInfo));
1✔
256
  }
1✔
257

258
  private AbstractChildrenIterator createChildrenIterator(N parent) {
259
    if (firstAncestorOfTraceback > -1) {
1✔
260
      // there may be traceback when try to find the matched state of node
261
      return new TraceBackChildrenIterator(parent, currentStateMatchInfo);
1✔
262
    } else if (currentStateMatchInfo.hasOnlyPreciseMatchTransition()) {
1✔
263
      // the child can be got directly with the precise value of transition
264
      return new PreciseMatchChildrenIterator(parent, currentStateMatchInfo);
1✔
265
    } else if (currentStateMatchInfo.hasNoPreciseMatchTransition()
1✔
266
        && currentStateMatchInfo.isSingleFuzzyMatchTransition()) {
1✔
267
      // only one transition which may match batch children, need to iterate and check all child
268
      return new SingleFuzzyMatchChildrenIterator(parent, currentStateMatchInfo);
1✔
269
    } else {
270
      // child may be matched by multi transitions, precise match or fuzzy match,
271
      // which results in one child match multi state; need to iterate and check all child
272
      return new MultiMatchTransitionChildrenIterator(parent, currentStateMatchInfo);
1✔
273
    }
274
  }
275

276
  private void popStack() {
277
    VisitorStackEntry stackEntry = visitorStack.pop();
1✔
278
    stackEntry.iterator.close();
1✔
279
    // The ancestor pop operation with level check supports the children of one node pushed by
280
    // batch.
281
    if (!visitorStack.isEmpty() && visitorStack.peek().level < ancestorStack.size()) {
1✔
282
      AncestorStackEntry ancestorStackEntry = ancestorStack.remove(ancestorStack.size() - 1);
1✔
283
      releaseNode(ancestorStackEntry.node);
1✔
284
      if (ancestorStack.size() <= firstAncestorOfTraceback) {
1✔
285
        firstAncestorOfTraceback = -1;
1✔
286
      }
287
    }
288
  }
1✔
289

290
  /**
291
   * Get full path of parent of current node. This method should be used in {@linkplain
292
   * AbstractTreeVisitor#acceptInternalMatchedNode}, {@linkplain
293
   * AbstractTreeVisitor#acceptFullMatchedNode},{@linkplain
294
   * AbstractTreeVisitor#shouldVisitSubtreeOfInternalMatchedNode} or {@linkplain
295
   * AbstractTreeVisitor#shouldVisitSubtreeOfFullMatchedNode}.
296
   *
297
   * @return full path from traverse start node to the parent of current node
298
   */
299
  protected PartialPath getParentPartialPath() {
300
    List<String> nodeNames = new ArrayList<>();
1✔
301
    Iterator<AncestorStackEntry> iterator = ancestorStack.iterator();
1✔
302
    for (int i = 0, size = shouldVisitSubtree ? ancestorStack.size() - 1 : ancestorStack.size();
1✔
303
        i < size;
1✔
304
        i++) {
1✔
305
      if (iterator.hasNext()) {
1✔
306
        nodeNames.add(iterator.next().node.getName());
1✔
307
      }
308
    }
309
    return new PartialPath(nodeNames.toArray(new String[0]));
1✔
310
  }
311

312
  /**
313
   * Get partial path from root to node.
314
   *
315
   * @param node node must be concluded in ancestorStack or nextMatchedNode
316
   * @return partial path from traverse start node to the specified node
317
   */
318
  protected final PartialPath getPartialPathFromRootToNode(N node) {
319
    return new PartialPath(getFullPathFromRootToNode(node));
1✔
320
  }
321

322
  /**
323
   * Get full path from root to node.
324
   *
325
   * @param node node must be concluded in ancestorStack or nextMatchedNode
326
   * @return full path from traverse start node to the specified node
327
   */
328
  protected final String[] getFullPathFromRootToNode(N node) {
329
    List<String> nodeNames = new ArrayList<>();
1✔
330
    for (AncestorStackEntry entry : ancestorStack) {
1✔
331
      nodeNames.add(entry.node.getName());
1✔
332
      if (entry.node == node) {
1✔
333
        return nodeNames.toArray(new String[0]);
1✔
334
      }
335
    }
1✔
336
    nodeNames.add(node.getName());
1✔
337
    return nodeNames.toArray(new String[0]);
1✔
338
  }
339

340
  protected final N getAncestorNodeByLevel(int level) {
341
    return ancestorStack.get(level).node;
1✔
342
  }
343

344
  protected final N getParentOfNextMatchedNode() {
345
    if (shouldVisitSubtree) {
1✔
346
      return ancestorStack.get(ancestorStack.size() - 2).node;
×
347
    } else {
348
      return ancestorStack.get(ancestorStack.size() - 1).node;
1✔
349
    }
350
  }
351

352
  /**
353
   * Get level from root to NextMatchedNode. Level of root is 0. For example, root.sg.d1.s1,
354
   * NextMatchedNode is s1, then return 3.
355
   *
356
   * @return level from root to NextMatchedNode
357
   */
358
  protected final int getLevelOfNextMatchedNode() {
359
    if (shouldVisitSubtree) {
1✔
360
      return ancestorStack.size() - 1;
1✔
361
    } else {
362
      return ancestorStack.size();
1✔
363
    }
364
  }
365

366
  protected final int getSizeOfAncestor() {
367
    return ancestorStack.size();
1✔
368
  }
369

370
  protected void setFailure(Throwable e) {
371
    this.throwable = e;
×
372
  }
×
373

374
  public Throwable getFailure() {
375
    return throwable;
×
376
  }
377

378
  public boolean isSuccess() {
379
    return throwable == null;
1✔
380
  }
381

382
  // Get a child with the given childName.
383
  protected abstract N getChild(N parent, String childName) throws Exception;
384

385
  // Get an iterator of all children.
386
  protected abstract Iterator<N> getChildrenIterator(N parent) throws Exception;
387

388
  // Get an iterator of specific children.
389
  protected abstract Iterator<N> getChildrenIterator(N parent, Iterator<String> childrenName)
390
      throws Exception;
391

392
  // Release a child node.
393
  protected void releaseNode(N node) {}
1✔
394

395
  // Release an iterator. It is not necessary to deal with all the elements in the iterator.
396
  // Only the elements that have been fetched but not returned by next() need to be released.
397
  protected void releaseNodeIterator(Iterator<N> nodeIterator) {}
1✔
398

399
  /**
400
   * Internal-match means the node matches an internal node name of the given path pattern. root.sg
401
   * internal match root.sg.**(pattern). This method should be implemented according to concrete
402
   * tasks.
403
   *
404
   * <p>Return whether the subtree of given node should be processed. If return true, the traversing
405
   * process will keep traversing the subtree. If return false, the traversing process will skip the
406
   * subtree of given node.
407
   */
408
  protected abstract boolean shouldVisitSubtreeOfInternalMatchedNode(N node);
409

410
  /**
411
   * Full-match means the node matches the last node name of the given path pattern. root.sg.d full
412
   * match root.sg.**(pattern) This method should be implemented according to concrete tasks.
413
   *
414
   * <p>Return whether the subtree of given node should be processed. If return true, the traversing
415
   * process will keep traversing the subtree. If return false, the traversing process will skip the
416
   * subtree of given node.
417
   */
418
  protected abstract boolean shouldVisitSubtreeOfFullMatchedNode(N node);
419

420
  /** Only accepted nodes will be considered for hasNext() and next() */
421
  protected abstract boolean acceptInternalMatchedNode(N node);
422

423
  /** Only accepted nodes will be considered for hasNext() and next() */
424
  protected abstract boolean acceptFullMatchedNode(N node);
425

426
  /** The method used for generating the result based on the matched node. */
427
  protected abstract R generateResult(N nextMatchedNode);
428

429
  private class VisitorStackEntry {
430

431
    // children iterator
432
    private final AbstractChildrenIterator iterator;
433

434
    // level of children taken from iterator, start from 1
435
    private final int level;
436

437
    VisitorStackEntry(AbstractChildrenIterator iterator, int level) {
1✔
438
      this.iterator = iterator;
1✔
439
      this.level = level;
1✔
440
    }
1✔
441
  }
442

443
  private class AncestorStackEntry {
444
    private final N node;
445

446
    private final IStateMatchInfo stateMatchInfo;
447

448
    AncestorStackEntry(N node, IStateMatchInfo stateMatchInfo) {
1✔
449
      this.node = node;
1✔
450
      this.stateMatchInfo = stateMatchInfo;
1✔
451
    }
1✔
452
  }
453

454
  protected final IFAState getNextMatchedScopeState(IFAState currentState, N node) {
455
    IFAState nextState = scopeDFA.getNextState(currentState, node.getName());
1✔
456
    if (nextState == null && node.getAlias() != null) {
1✔
457
      return scopeDFA.getNextState(currentState, node.getAlias());
1✔
458
    }
459
    return nextState;
1✔
460
  }
461

462
  // implement common iterating logic of different children iterator
463
  private abstract class AbstractChildrenIterator implements Iterator<N> {
464
    protected final N parent;
465
    protected final IFAState currentScopeState;
466
    private N nextMatchedChild;
467

468
    protected AbstractChildrenIterator(N parent, IFAState currentScopeState) {
1✔
469
      this.parent = parent;
1✔
470
      this.currentScopeState = currentScopeState;
1✔
471
    }
1✔
472

473
    @Override
474
    public boolean hasNext() {
475
      if (nextMatchedChild == null) {
1✔
476
        try {
477
          getNext();
1✔
478
        } catch (Throwable e) {
×
479
          logger.warn(e.getMessage(), e);
×
480
          throw new RuntimeException(e.getMessage(), e);
×
481
        }
1✔
482
      }
483
      return nextMatchedChild != null;
1✔
484
    }
485

486
    @Override
487
    public N next() {
488
      if (!hasNext()) {
1✔
489
        throw new NoSuchElementException();
×
490
      }
491
      N result = nextMatchedChild;
1✔
492
      nextMatchedChild = null;
1✔
493
      return result;
1✔
494
    }
495

496
    protected final void saveResult(N child, IStateMatchInfo stateMatchInfo) {
497
      nextMatchedChild = child;
1✔
498
      currentStateMatchInfo = stateMatchInfo;
1✔
499
    }
1✔
500

501
    protected abstract void getNext() throws Exception;
502

503
    protected final Iterator<N> initChildrenIterator() throws Exception {
504
      if (!allScope && scopeDFA.getFuzzyMatchTransitionSize(currentScopeState) == 0) {
1✔
505
        return getChildrenIterator(
1✔
506
            parent, scopeDFA.getPreciseMatchTransition(currentScopeState).keySet().iterator());
1✔
507
      } else {
508
        return getChildrenIterator(parent);
1✔
509
      }
510
    }
511

512
    protected void close() {
513
      if (nextMatchedChild != null) {
1✔
514
        releaseNode(nextMatchedChild);
×
515
      }
516
    }
1✔
517
  }
518

519
  // the child can be got directly with the precise value of transition, there's no traceback
520
  private class PreciseMatchChildrenIterator extends AbstractChildrenIterator {
521
    private final IFAState sourceState;
522
    private final Iterator<IFATransition> transitionIterator;
523

524
    private PreciseMatchChildrenIterator(N parent, IStateMatchInfo stateMatchInfo) {
1✔
525
      super(parent, stateMatchInfo.getScopeMatchedState());
1✔
526
      this.sourceState = stateMatchInfo.getOneMatchedState();
1✔
527
      transitionIterator = patternFA.getPreciseMatchTransitionIterator(sourceState);
1✔
528
    }
1✔
529

530
    @Override
531
    protected void getNext() throws Exception {
532
      IFATransition transition;
533
      while (transitionIterator.hasNext()) {
1✔
534
        transition = transitionIterator.next();
1✔
535
        N child = getChild(parent, transition.getAcceptEvent());
1✔
536
        if (child == null) {
1✔
537
          continue;
1✔
538
        }
539
        IFAState nextScopeState =
540
            allScope ? null : getNextMatchedScopeState(currentScopeState, child);
1✔
541
        if (!allScope && nextScopeState == null) {
1✔
542
          releaseNode(child);
1✔
543
          continue;
1✔
544
        }
545
        saveResult(
1✔
546
            child,
547
            new StateSingleMatchInfo(
548
                patternFA, patternFA.getNextState(sourceState, transition), nextScopeState));
1✔
549
        return;
1✔
550
      }
551
    }
1✔
552
  }
553

554
  // only one fuzzy transition which may match batch children, need to iterate and check all
555
  // children,
556
  // there's no traceback
557
  private class SingleFuzzyMatchChildrenIterator extends AbstractChildrenIterator {
558

559
    private final IFAState sourceState;
560
    private final IFATransition transition;
561
    private Iterator<N> childrenIterator;
562

563
    private SingleFuzzyMatchChildrenIterator(N parent, IStateMatchInfo stateMatchInfo) {
1✔
564
      super(parent, stateMatchInfo.getScopeMatchedState());
1✔
565
      this.sourceState = stateMatchInfo.getOneMatchedState();
1✔
566
      this.transition = patternFA.getFuzzyMatchTransitionIterator(sourceState).next();
1✔
567
    }
1✔
568

569
    @Override
570
    protected void getNext() throws Exception {
571
      if (childrenIterator == null) {
1✔
572
        this.childrenIterator = initChildrenIterator();
1✔
573
      }
574
      N child;
575
      while (childrenIterator.hasNext()) {
1✔
576
        child = childrenIterator.next();
1✔
577
        if (tryGetNextState(child, sourceState, transition) == null) {
1✔
578
          releaseNode(child);
1✔
579
          continue;
1✔
580
        }
581
        IFAState nextScopeState =
582
            allScope ? null : getNextMatchedScopeState(currentScopeState, child);
1✔
583
        saveResult(
1✔
584
            child,
585
            new StateSingleMatchInfo(
586
                patternFA, patternFA.getNextState(sourceState, transition), nextScopeState));
1✔
587
        return;
1✔
588
      }
589
    }
1✔
590

591
    @Override
592
    protected void close() {
593
      super.close();
1✔
594
      if (childrenIterator != null) {
1✔
595
        releaseNodeIterator(childrenIterator);
1✔
596
      }
597
    }
1✔
598
  }
599

600
  // child may be matched by multi transitions, precise match or fuzzy match,
601
  // which results in one child match multi state; need to iterate and check all child.
602
  // the iterating process will try to get the first matched state of a child, and if there are some
603
  // rest transitions, there may be traceback when checking the descendents
604
  private class MultiMatchTransitionChildrenIterator extends AbstractChildrenIterator {
605

606
    private final IFAState sourceState;
607
    private final Map<String, IFATransition> preciseMatchTransitionMap;
608
    private Iterator<N> iterator;
609

610
    private MultiMatchTransitionChildrenIterator(N parent, IStateMatchInfo stateMatchInfo) {
1✔
611
      super(parent, stateMatchInfo.getScopeMatchedState());
1✔
612
      this.sourceState = stateMatchInfo.getOneMatchedState();
1✔
613
      this.preciseMatchTransitionMap = patternFA.getPreciseMatchTransition(sourceState);
1✔
614
    }
1✔
615

616
    @Override
617
    protected void getNext() throws Exception {
618
      if (iterator == null) {
1✔
619
        this.iterator = initChildrenIterator();
1✔
620
      }
621
      N child;
622

623
      IFAState matchedState = null;
1✔
624
      Iterator<IFATransition> transitionIterator;
625
      IStateMatchInfo stateMatchInfo;
626
      while (iterator.hasNext()) {
1✔
627
        child = iterator.next();
1✔
628
        IFAState nextScopeState =
629
            allScope ? null : getNextMatchedScopeState(currentScopeState, child);
1✔
630
        // find first matched state
631
        if (!preciseMatchTransitionMap.isEmpty()) {
1✔
632
          matchedState = tryGetNextState(child, sourceState, preciseMatchTransitionMap);
1✔
633
        }
634
        transitionIterator = patternFA.getFuzzyMatchTransitionIterator(sourceState);
1✔
635
        if (matchedState == null) {
1✔
636
          while (transitionIterator.hasNext()) {
1✔
637
            matchedState = tryGetNextState(child, sourceState, transitionIterator.next());
1✔
638
            if (matchedState != null) {
1✔
639
              break;
1✔
640
            }
641
          }
642
          if (matchedState == null) {
1✔
643
            releaseNode(child);
1✔
644
            continue;
1✔
645
          }
646
        }
647

648
        // check whether accept the first matched state
649
        if (mayTargetNodeType(child) && !matchedState.isFinal()) {
1✔
650
          // not accept the first matched state since this node may be a target result, check the
651
          // other states
652
          if (patternFA.mayTransitionOverlap() && transitionIterator.hasNext()) {
1✔
653
            stateMatchInfo =
×
654
                new StateMultiMatchInfo(
655
                    patternFA, matchedState, transitionIterator, nextScopeState);
656
            firstAncestorOfTraceback = ancestorStack.size();
×
657

658
            while (transitionIterator.hasNext()) {
×
659
              matchedState = tryGetNextState(child, sourceState, transitionIterator.next());
×
660
              if (matchedState != null) {
×
661
                stateMatchInfo.addMatchedState(matchedState);
×
662
                if (matchedState.isFinal()) {
×
663
                  break;
×
664
                }
665
              }
666
            }
667
          } else {
668
            stateMatchInfo = new StateSingleMatchInfo(patternFA, matchedState, nextScopeState);
1✔
669
          }
670
        } else {
671
          // accept the first matched state, directly save it
672
          if (patternFA.mayTransitionOverlap() && transitionIterator.hasNext()) {
1✔
673
            stateMatchInfo =
1✔
674
                new StateMultiMatchInfo(
675
                    patternFA, matchedState, transitionIterator, nextScopeState);
676
            firstAncestorOfTraceback = ancestorStack.size();
1✔
677
          } else {
678
            stateMatchInfo = new StateSingleMatchInfo(patternFA, matchedState, nextScopeState);
1✔
679
          }
680
        }
681

682
        saveResult(child, stateMatchInfo);
1✔
683
        return;
1✔
684
      }
685
    }
1✔
686

687
    @Override
688
    protected void close() {
689
      super.close();
1✔
690
      if (iterator != null) {
1✔
691
        releaseNodeIterator(iterator);
1✔
692
      }
693
    }
1✔
694
  }
695

696
  // there may be traceback when try to find the matched state of node;
697
  // the iterating process will try to get the first matched state of a child.
698
  private class TraceBackChildrenIterator extends AbstractChildrenIterator {
699

700
    private final IStateMatchInfo sourceStateMatchInfo;
701
    private Iterator<N> iterator;
702

703
    TraceBackChildrenIterator(N parent, IStateMatchInfo stateMatchInfo) {
1✔
704
      super(parent, stateMatchInfo.getScopeMatchedState());
1✔
705
      this.sourceStateMatchInfo = stateMatchInfo;
1✔
706
    }
1✔
707

708
    @Override
709
    protected void getNext() throws Exception {
710
      if (iterator == null) {
1✔
711
        this.iterator = initChildrenIterator();
1✔
712
      }
713
      N child;
714

715
      IFAState sourceState;
716

717
      IStateMatchInfo stateMatchInfo;
718
      Iterator<IFATransition> transitionIterator;
719

720
      while (iterator.hasNext()) {
1✔
721

722
        child = iterator.next();
1✔
723
        IFAState nextScopeState =
724
            allScope ? null : getNextMatchedScopeState(currentScopeState, child);
1✔
725

726
        stateMatchInfo = new StateMultiMatchInfo(patternFA, nextScopeState);
1✔
727
        if (mayTargetNodeType(child)) {
1✔
728
          for (int i = 0; i < sourceStateMatchInfo.getMatchedStateSize(); i++) {
1✔
729
            sourceState = sourceStateMatchInfo.getMatchedState(i);
1✔
730
            transitionIterator = tryGetNextMatchedState(child, sourceState, stateMatchInfo, true);
1✔
731
            if (stateMatchInfo.getMatchedStateSize() > 0) {
1✔
732
              stateMatchInfo.setSourceStateOrdinal(i);
1✔
733
              stateMatchInfo.setSourceTransitionIterator(transitionIterator);
1✔
734
              if (stateMatchInfo.hasFinalState()) {
1✔
735
                break;
1✔
736
              }
737
            }
738
          }
739

740
          if (stateMatchInfo.getMatchedStateSize() == 0 || !stateMatchInfo.hasFinalState()) {
1✔
741
            traceback(child, stateMatchInfo, sourceStateMatchInfo.getMatchedStateSize() - 1, true);
1✔
742
            if (stateMatchInfo.getMatchedStateSize() == 0) {
1✔
743
              releaseNode(child);
1✔
744
              continue;
1✔
745
            }
746
          }
747
        } else {
748
          for (int i = 0; i < sourceStateMatchInfo.getMatchedStateSize(); i++) {
1✔
749
            sourceState = sourceStateMatchInfo.getMatchedState(i);
1✔
750
            transitionIterator = tryGetNextMatchedState(child, sourceState, stateMatchInfo, false);
1✔
751
            if (stateMatchInfo.getMatchedStateSize() > 0) {
1✔
752
              stateMatchInfo.setSourceStateOrdinal(i);
1✔
753
              stateMatchInfo.setSourceTransitionIterator(transitionIterator);
1✔
754
              break;
1✔
755
            }
756
          }
757

758
          if (stateMatchInfo.getMatchedStateSize() == 0) {
1✔
759
            traceback(child, stateMatchInfo, sourceStateMatchInfo.getMatchedStateSize() - 1, false);
1✔
760
            if (stateMatchInfo.getMatchedStateSize() == 0) {
1✔
761
              releaseNode(child);
1✔
762
              continue;
1✔
763
            }
764
          }
765
        }
766

767
        saveResult(child, stateMatchInfo);
1✔
768
        return;
1✔
769
      }
770
    }
1✔
771

772
    /**
773
     * Try to get next matched state from sourceState and add it into currentStateMatchInfo
774
     *
775
     * @param child child node to match
776
     * @param sourceState source state
777
     * @param currentStateMatchInfo currentStateMatchInfo
778
     * @return iterator of rest transitions
779
     */
780
    private Iterator<IFATransition> tryGetNextMatchedState(
781
        N child,
782
        IFAState sourceState,
783
        IStateMatchInfo currentStateMatchInfo,
784
        boolean needFinalState) {
785
      Map<String, IFATransition> preciseMatchTransitionMap =
1✔
786
          patternFA.getPreciseMatchTransition(sourceState);
1✔
787

788
      IFAState matchedState;
789
      if (!preciseMatchTransitionMap.isEmpty()) {
1✔
790
        matchedState = tryGetNextState(child, sourceState, preciseMatchTransitionMap);
×
791
        if (matchedState != null) {
×
792
          currentStateMatchInfo.addMatchedState(matchedState);
×
793
          if (!needFinalState || matchedState.isFinal()) {
×
794
            return patternFA.getFuzzyMatchTransitionIterator(sourceState);
×
795
          }
796
        }
797
      }
798

799
      Iterator<IFATransition> transitionIterator =
1✔
800
          patternFA.getFuzzyMatchTransitionIterator(sourceState);
1✔
801
      while (transitionIterator.hasNext()) {
1✔
802
        matchedState = tryGetNextState(child, sourceState, transitionIterator.next());
1✔
803
        if (matchedState != null) {
1✔
804
          currentStateMatchInfo.addMatchedState(matchedState);
1✔
805
          if (!needFinalState || matchedState.isFinal()) {
1✔
806
            return transitionIterator;
1✔
807
          }
808
        }
809
      }
810
      return transitionIterator;
1✔
811
    }
812

813
    private void traceback(
814
        N node,
815
        IStateMatchInfo stateMatchInfo,
816
        int checkedSourceStateOrdinal,
817
        boolean needFinalState) {
818
      IStateMatchInfo parentStateMatchInfo;
819

820
      N currentNode;
821
      IStateMatchInfo currentStateMatchInfo;
822

823
      int sourceStateOrdinal;
824
      IFAState sourceState = null;
1✔
825
      Iterator<IFATransition> transitionIterator = null;
1✔
826

827
      int matchedStateSize;
828
      IFAState matchedState;
829

830
      int currentNodeIndex;
831
      for (int i = ancestorStack.size() - 1; i >= firstAncestorOfTraceback; i--) {
1✔
832
        parentStateMatchInfo = ancestorStack.get(i - 1).stateMatchInfo;
1✔
833
        currentStateMatchInfo = ancestorStack.get(i).stateMatchInfo;
1✔
834

835
        // there's no state not further searched
836
        if (currentStateMatchInfo.getSourceStateOrdinal()
1✔
837
            == parentStateMatchInfo.getMatchedStateSize()) {
1✔
838
          continue;
×
839
        }
840

841
        // there's some state not further searched, process them in order
842
        currentNodeIndex = i;
1✔
843
        while (currentNodeIndex >= i) {
1✔
844
          parentStateMatchInfo = ancestorStack.get(currentNodeIndex - 1).stateMatchInfo;
1✔
845

846
          if (currentNodeIndex == ancestorStack.size()) {
1✔
847
            currentNode = node;
1✔
848
            currentStateMatchInfo = stateMatchInfo;
1✔
849
          } else {
850
            currentNode = ancestorStack.get(currentNodeIndex).node;
1✔
851
            currentStateMatchInfo = ancestorStack.get(currentNodeIndex).stateMatchInfo;
1✔
852
          }
853

854
          matchedState = null;
1✔
855
          if (currentNode == node) {
1✔
856
            sourceStateOrdinal = checkedSourceStateOrdinal;
1✔
857
          } else {
858
            sourceStateOrdinal = currentStateMatchInfo.getSourceStateOrdinal();
1✔
859
            if (sourceStateOrdinal == parentStateMatchInfo.getMatchedStateSize()) {
1✔
860
              currentNodeIndex--;
×
861
              continue;
×
862
            }
863
            // there may be some states could be matched from transition of current source state
864
            sourceState = parentStateMatchInfo.getMatchedState(sourceStateOrdinal);
1✔
865
            transitionIterator = currentStateMatchInfo.getSourceTransitionIterator();
1✔
866
            while (transitionIterator.hasNext()) {
1✔
867
              matchedState = tryGetNextState(currentNode, sourceState, transitionIterator.next());
1✔
868
              if (matchedState != null) {
1✔
869
                break;
1✔
870
              }
871
            }
872
          }
873

874
          if (matchedState == null) {
1✔
875
            while (++sourceStateOrdinal < parentStateMatchInfo.getMatchedStateSize()) {
1✔
876
              sourceState = parentStateMatchInfo.getMatchedState(sourceStateOrdinal);
1✔
877
              matchedStateSize = currentStateMatchInfo.getMatchedStateSize();
1✔
878
              transitionIterator =
1✔
879
                  tryGetNextMatchedState(
1✔
880
                      currentNode, sourceState, currentStateMatchInfo, needFinalState);
881
              // change of matchedStateSize means currentNode there is transition from sourceState
882
              // matching currentNode
883
              if (matchedStateSize != currentStateMatchInfo.getMatchedStateSize()) {
1✔
884
                matchedState = currentStateMatchInfo.getMatchedState(matchedStateSize);
1✔
885
                currentStateMatchInfo.setSourceStateOrdinal(sourceStateOrdinal);
1✔
886
                currentStateMatchInfo.setSourceTransitionIterator(transitionIterator);
1✔
887
                break;
1✔
888
              }
889
            }
890
            if (matchedState == null) {
1✔
891
              currentStateMatchInfo.setSourceStateOrdinal(sourceStateOrdinal - 1);
1✔
892
              currentStateMatchInfo.setSourceTransitionIterator(transitionIterator);
1✔
893
              currentNodeIndex--;
1✔
894
              continue;
1✔
895
            }
896
          }
897

898
          currentStateMatchInfo.addMatchedState(matchedState);
1✔
899

900
          if (currentNode == node) {
1✔
901
            if (needFinalState && !currentStateMatchInfo.hasFinalState()) {
1✔
902
              while (transitionIterator.hasNext()) {
×
903
                matchedState = tryGetNextState(currentNode, sourceState, transitionIterator.next());
×
904
                if (matchedState != null) {
×
905
                  currentStateMatchInfo.addMatchedState(matchedState);
×
906
                  if (matchedState.isFinal()) {
×
907
                    return;
×
908
                  }
909
                }
910
              }
911
              currentNodeIndex--;
×
912
            } else {
913
              return;
1✔
914
            }
915
          } else {
916
            currentNodeIndex++;
1✔
917
          }
918
        }
919
      }
920
    }
1✔
921

922
    @Override
923
    protected void close() {
924
      super.close();
1✔
925
      if (iterator != null) {
1✔
926
        releaseNodeIterator(iterator);
1✔
927
      }
928
    }
1✔
929
  }
930

931
  // the match process of FA graph is a dfs on FA Graph
932

933
  // a tmp way to process alias of measurement node, which may results in multi event when checking
934
  // the transition;
935
  // fortunately, the measurement node only match the final state, which means there won't be any
936
  // multi transition and traceback judge
937
  protected IFAState tryGetNextState(
938
      N node, IFAState sourceState, Map<String, IFATransition> preciseMatchTransitionMap) {
939
    IFATransition transition = preciseMatchTransitionMap.get(node.getName());
1✔
940
    if (transition == null) {
1✔
941
      return null;
1✔
942
    }
943
    return patternFA.getNextState(sourceState, transition);
×
944
  }
945

946
  // a tmp way to process alias of measurement node, which may results in multi event when checking
947
  // the transition;
948
  // fortunately, the measurement node only match the final state, which means there won't be any
949
  // multi transition and traceback judge
950
  protected IFAState tryGetNextState(N node, IFAState sourceState, IFATransition transition) {
951
    if (transition.isMatch(node.getName())) {
1✔
952
      return patternFA.getNextState(sourceState, transition);
1✔
953
    } else {
954
      return null;
×
955
    }
956
  }
957

958
  /**
959
   * May node can be accepted if it reaches final state. Its implementation should not depend on the
960
   * context.
961
   *
962
   * @param node node to be checked
963
   * @return false is if node must not be accepted. Otherwise, return true.
964
   */
965
  protected abstract boolean mayTargetNodeType(N node);
966
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc