Java Code Examples for java.util.ArrayDeque#push()

The following examples show how to use java.util.ArrayDeque#push() . These examples are extracted from open source projects. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source Project: apiman   File: HeaderMapDeserializer.java    License: Apache License 2.0 6 votes vote down vote up
/**
 * @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
 */
@Override
public HeaderMap deserialize(JsonParser p, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
    HeaderMap map = new HeaderMap();

    while (p.nextToken() != JsonToken.END_OBJECT) {
        String name = p.getCurrentName();

        p.nextToken();

        if (p.currentToken().isScalarValue()) {
            map.add(name, p.getValueAsString());
        } else {
            ArrayDeque<String> values = new ArrayDeque<>();
            while (p.nextToken() != JsonToken.END_ARRAY) {
                values.push(p.getValueAsString());
            }
            values.forEach(value -> map.add(name, value));
        }
    }
    return map;
}
 
Example 2
Source Project: buck   File: AbstractSkylarkFileParser.java    License: Apache License 2.0 6 votes vote down vote up
/**
 * Updates extension load state with the list of its dependencies, and schedules any unsatisfied
 * dependencies to be loaded by adding those dependencies to the work queue.
 *
 * @param load {@link ExtensionLoadState} representing extension currently loaded
 * @param queue a work queue of extensions that still need to be loaded.
 * @return true if this extension has any unsatisfied dependencies
 */
private boolean processExtensionDependencies(
    ExtensionLoadState load, ArrayDeque<ExtensionLoadState> queue) {
  // Update this load state with the list of its dependencies.
  // Schedule missing dependencies to be loaded.
  boolean haveUnsatisfiedDeps = false;
  for (int i = 0; i < load.getAST().getImports().size(); ++i) {
    LoadImport dependency =
        ImmutableLoadImport.of(load.getLabel(), load.getAST().getImports().get(i));

    // Record dependency for this load.
    load.addDependency(dependency);
    com.google.devtools.build.lib.vfs.Path extensionPath =
        getImportPath(dependency.getLabel(), dependency.getImport());
    if (extensionDataCache.getIfPresent(extensionPath) == null) {
      // Schedule dependency to be loaded if needed.
      haveUnsatisfiedDeps = true;
      queue.push(new ExtensionLoadState(dependency, extensionPath));
    }
  }
  return haveUnsatisfiedDeps;
}
 
Example 3
/**
 * Find all contiguous blocks, set in target and clear in source
 */
private int floodFillTarget(TileFlags src, TileFlags dest, int x, int y) {
    int cnt = 0;
    ArrayDeque<int[]> stack = new ArrayDeque<int[]>();
    stack.push(new int[] { x, y });
    
    while(stack.isEmpty() == false) {
        int[] nxt = stack.pop();
        x = nxt[0];
        y = nxt[1];
        if(src.getFlag(x, y)) { /* Set in src */
            src.setFlag(x, y, false);   /* Clear source */
            dest.setFlag(x, y, true);   /* Set in destination */
            cnt++;
            if(src.getFlag(x+1, y))
                stack.push(new int[] { x+1, y });
            if(src.getFlag(x-1, y))
                stack.push(new int[] { x-1, y });
            if(src.getFlag(x, y+1))
                stack.push(new int[] { x, y+1 });
            if(src.getFlag(x, y-1))
                stack.push(new int[] { x, y-1 });
        }
    }
    return cnt;
}
 
Example 4
/**
 * Locate a late inline call site: find, in this instance's
 * {@linkplain #calls call sites}, the one furthest down the given call
 * stack.
 *
 * Multiple chains of identical call sites with the same method name / bci
 * combination are possible, so we have to try them all until we find the
 * late inline call site that has a matching inline ID.
 *
 * @return a matching call site, or {@code null} if none was found.
 */
public CallSite findCallSite(ArrayDeque<CallSite> sites) {
    if (calls == null) {
        return null;
    }
    CallSite site = sites.pop();
    for (CallSite c : calls) {
        if (c.matches(site)) {
            if (!sites.isEmpty()) {
                CallSite res = c.findCallSite(sites);
                if (res != null) {
                    sites.push(site);
                    return res;
                }
            } else {
                sites.push(site);
                return c;
            }
        }
    }
    sites.push(site);
    return null;
}
 
Example 5
/**
 * Return a {@link List} with the last count of log statements. If there haven't been any yet, an empty list is returned.
 */
public final List<String> getLastLogs(int count)
{
    if (count > m_logList.size())
    {
        count = m_logList.size();
    }
    if (m_logList.size() == 0)
    {
        return new ArrayList<>(0);
    }
    else
    {
        final ArrayDeque<String> list = new ArrayDeque<>(count);
        count--;
        int start = m_logList.size() - 1;
        int end = start - count;
        for (int i = start; i >= end; i--)
        {

            list.push(m_logList.get(i));
        }
        return new ArrayList<>(list);
    }
}
 
Example 6
Source Project: EasyMPermission   File: JavacResolution.java    License: MIT License 6 votes vote down vote up
public Map<JCTree, JCTree> resolveMethodMember(JavacNode node) {
	ArrayDeque<JCTree> stack = new ArrayDeque<JCTree>();
	
	{
		JavacNode n = node;
		while (n != null) {
			stack.push(n.get());
			n = n.up();
		}
	}
	
	messageSuppressor.disableLoggers();
	try {
		EnvFinder finder = new EnvFinder(node.getContext());
		while (!stack.isEmpty()) stack.pop().accept(finder);
		
		TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker(), node.getContext());
		JCTree copy = mirrorMaker.copy(finder.copyAt());
		
		memberEnterAndAttribute(copy, finder.get(), node.getContext());
		return mirrorMaker.getOriginalToCopyMap();
	} finally {
		messageSuppressor.enableLoggers();
	}
}
 
Example 7
Source Project: EasyMPermission   File: JavacResolution.java    License: MIT License 6 votes vote down vote up
public void resolveClassMember(JavacNode node) {
	ArrayDeque<JCTree> stack = new ArrayDeque<JCTree>();
	
	{
		JavacNode n = node;
		while (n != null) {
			stack.push(n.get());
			n = n.up();
		}
	}
	
	messageSuppressor.disableLoggers();
	try {
		EnvFinder finder = new EnvFinder(node.getContext());
		while (!stack.isEmpty()) stack.pop().accept(finder);
		
		attrib(node.get(), finder.get());
	} finally {
		messageSuppressor.enableLoggers();
	}
}
 
Example 8
/**
 * Returns the list of tables that one must process before processing the provided {@code table}.
 *
 * @param table The table that you wish to process
 * @return the ordered {@link ArrayDeque} of tables to process before processing {@code table}
 */
public Collection<Table> getDependentTables(Table table) {
  ArrayDeque<Table> tableStack = new ArrayDeque<>();
  while (table != null && !processedTables.contains(table)) {
    tableStack.push(table);
    processedTables.add(table);

    table = tableDependencies.get(table);
  }

  return tableStack;
}
 
Example 9
Source Project: netbeans   File: VariousUtils.java    License: Apache License 2.0 5 votes vote down vote up
private static void createVariableBaseChain(VariableBase node, ArrayDeque<VariableBase> stack) {
    stack.push(node);
    if (node instanceof MethodInvocation) {
        createVariableBaseChain(((MethodInvocation) node).getDispatcher(), stack);
    } else if (node instanceof FieldAccess) {
        createVariableBaseChain(((FieldAccess) node).getDispatcher(), stack);
    } else if (node instanceof StaticDispatch) {
        Expression dispatcher = ((StaticDispatch) node).getDispatcher();
        if (dispatcher instanceof VariableBase) {
            createVariableBaseChain((VariableBase) dispatcher, stack);
        }
    }
}
 
Example 10
Source Project: buck   File: AbstractSkylarkFileParser.java    License: Apache License 2.0 5 votes vote down vote up
/**
 * Creates an extension from a {@code path}.
 *
 * @param loadImport an import label representing an extension to load.
 */
private ExtensionData loadExtension(LoadImport loadImport)
    throws IOException, BuildFileParseException, InterruptedException {
  ExtensionData extension = null;
  ArrayDeque<ExtensionLoadState> work = new ArrayDeque<>();
  work.push(
      new ExtensionLoadState(
          loadImport, getImportPath(loadImport.getLabel(), loadImport.getImport())));

  while (!work.isEmpty()) {
    ExtensionLoadState load = work.peek();
    extension =
        lookupExtensionForImport(load.getPath(), load.getSkylarkImport().getImportString());

    if (extension != null) {
      // It's possible that some lower level dependencies already loaded
      // this work item.  We're done with it, so pop the queue.
      work.pop();
      continue;
    }

    // Load BuildFileAST if needed.
    boolean astLoaded = maybeLoadAST(load);
    boolean haveUnsatisfiedDeps = astLoaded && processExtensionDependencies(load, work);

    // NB: If we have unsatisfied dependencies, we don't do anything;
    // more importantly we do not pop the work queue in this case.
    // This load is kept on the queue until all of its dependencies are satisfied.

    if (!haveUnsatisfiedDeps) {
      // We are done with this load; build it and cache it.
      work.removeFirst();
      extension = buildExtensionData(load);
      extensionDataCache.put(load.getPath(), extension);
    }
  }

  Preconditions.checkNotNull(extension);
  return extension;
}
 
Example 11
/**
 * push(null) throws NPE
 */
public void testPushNull() {
    ArrayDeque q = new ArrayDeque(1);
    try {
        q.push(null);
        shouldThrow();
    } catch (NullPointerException success) {}
}
 
Example 12
Source Project: Logistics-Pipes-2   File: DijkstraRouter.java    License: MIT License 5 votes vote down vote up
private void pushToRouteUntillParent(NetworkNode current, ArrayDeque<Tuple<UUID, EnumFacing>> route) throws InterruptedException {
	NetworkNode parent = current.parent.getKey();
	EnumFacing direction = current.parent.getVal();
	int parentDirection = direction.getOpposite().getIndex();

	NetworkNode help = current;
	while(help.getId() != parent.getId()) {
		help = help.getNeighborAt(parentDirection);
		route.push(new Tuple<UUID, EnumFacing>(help.getId(), direction));
		//help.getMember().spawnParticle(1.0f, 0.549f, 0.0f);
		Thread.sleep(120);
	}
}
 
Example 13
@Override
public void setUnspecifiedElementNamespace(final String namespace) {
    ArrayDeque<String> namespaces = this.unspecifiedNamespaces;
    namespaces.pop();
    namespaces.push(namespace == null ? NO_NAMESPACE : namespace);
}
 
Example 14
private void fillDep(Grid grid) {
    final int nbrCols = grid.getCols();
    final int nbrRows = grid.getRows();

    // flag for each grid cell to indicate whether it has been treated
    BitSet closed = new BitSet(nbrCols * nbrRows);

    // count the number of processed cells for the progress indicator
    int nbrCellsProcessed = 0;

    // plain queue
    final int expectedMaxPlainSize = grid.getCols();
    ArrayDeque<Cell> pit = new ArrayDeque(expectedMaxPlainSize);

    // priority queue with cells ordered by elevation. Lowest elevation is 
    // the head of the queue.
    PriorityQueue<Cell> open = new PriorityQueue(nbrCols * 2 + nbrRows * 2);

    // add edge cells and void cells to priority queue
    for (int row = 0; row < nbrRows && reportProgress(0); row++) {
        for (int col = 0; col < nbrCols; col++) {
            if (grid.isVoid(col, row)) {
                continue;
            }
            if (isCellOnGridBorder(grid, col, row)) {
                closed.set(col + row * nbrCols);
                open.add(new Cell(col, row, grid.getValue(col, row)));
            }
        }
    }

    while (!open.isEmpty() || !pit.isEmpty()) {
        // either consider the point with the lowest elevation in the queue
        // or consider a point in/near a "pit" which is being filled
        final Cell c = !pit.isEmpty() ? pit.pop() : open.poll();
        ++nbrCellsProcessed;

        // look at neighbours of c
        for (int i = 1; i <= 8; i++) {
            final int nRow = c.row + DY[i];
            final int nCol = c.col + DX[i];

            // ensure neighbour is within the bounds of the grid
            if (nRow < 0 || nCol < 0 || nRow >= nbrRows || nCol >= nbrCols) {
                continue;
            }
            // ensure we haven't visited the cell yet
            if (closed.get(nCol + nbrCols * nRow)) {
                continue;
            }

            closed.set(nCol + nbrCols * nRow);
            final float n = grid.getValue(nCol, nRow);

            // ensure the cell is valid
            if (Float.isNaN(n)) {
                continue;
            }

            // if the current elevation is greater than the neighbour
            // we are entering a depression therefore we add to the 
            // plain queue otherwise, add to the open queue
            if (n <= c.elevation) {
                if (n < c.elevation) {
                    // found a cell in a pit
                    grid.setValue(c.elevation, nCol, nRow);
                }
                pit.push(new Cell(nCol, nRow, c.elevation));
            } else {
                open.add(new Cell(nCol, nRow, n));
            }
        }

        if (nbrCellsProcessed % 50000 == 0) {
            if (!reportProgress(100f * nbrCellsProcessed / (nbrCols * nbrRows))) {
                return;
            }
        }
    }
}
 
Example 15
/**
 * Parses the text payload of a WebVTT Cue and applies modifications on {@link WebvttCue.Builder}.
 *
 * @param id Id of the cue, {@code null} if it is not present.
 * @param markup The markup text to be parsed.
 * @param styles List of styles defined by the CSS style blocks preceeding the cues.
 * @param builder Output builder.
 */
/* package */ static void parseCueText(String id, String markup, WebvttCue.Builder builder,
    List<WebvttCssStyle> styles) {
  SpannableStringBuilder spannedText = new SpannableStringBuilder();
  ArrayDeque<StartTag> startTagStack = new ArrayDeque<>();
  List<StyleMatch> scratchStyleMatches = new ArrayList<>();
  int pos = 0;
  while (pos < markup.length()) {
    char curr = markup.charAt(pos);
    switch (curr) {
      case CHAR_LESS_THAN:
        if (pos + 1 >= markup.length()) {
          pos++;
          break; // avoid ArrayOutOfBoundsException
        }
        int ltPos = pos;
        boolean isClosingTag = markup.charAt(ltPos + 1) == CHAR_SLASH;
        pos = findEndOfTag(markup, ltPos + 1);
        boolean isVoidTag = markup.charAt(pos - 2) == CHAR_SLASH;
        String fullTagExpression = markup.substring(ltPos + (isClosingTag ? 2 : 1),
            isVoidTag ? pos - 2 : pos - 1);
        String tagName = getTagName(fullTagExpression);
        if (tagName == null || !isSupportedTag(tagName)) {
          continue;
        }
        if (isClosingTag) {
          StartTag startTag;
          do {
            if (startTagStack.isEmpty()) {
              break;
            }
            startTag = startTagStack.pop();
            applySpansForTag(id, startTag, spannedText, styles, scratchStyleMatches);
          } while(!startTag.name.equals(tagName));
        } else if (!isVoidTag) {
          startTagStack.push(StartTag.buildStartTag(fullTagExpression, spannedText.length()));
        }
        break;
      case CHAR_AMPERSAND:
        int semiColonEndIndex = markup.indexOf(CHAR_SEMI_COLON, pos + 1);
        int spaceEndIndex = markup.indexOf(CHAR_SPACE, pos + 1);
        int entityEndIndex = semiColonEndIndex == -1 ? spaceEndIndex
            : (spaceEndIndex == -1 ? semiColonEndIndex
                : Math.min(semiColonEndIndex, spaceEndIndex));
        if (entityEndIndex != -1) {
          applyEntity(markup.substring(pos + 1, entityEndIndex), spannedText);
          if (entityEndIndex == spaceEndIndex) {
            spannedText.append(" ");
          }
          pos = entityEndIndex + 1;
        } else {
          spannedText.append(curr);
          pos++;
        }
        break;
      default:
        spannedText.append(curr);
        pos++;
        break;
    }
  }
  // apply unclosed tags
  while (!startTagStack.isEmpty()) {
    applySpansForTag(id, startTagStack.pop(), spannedText, styles, scratchStyleMatches);
  }
  applySpansForTag(id, StartTag.buildWholeCueVirtualTag(), spannedText, styles,
      scratchStyleMatches);
  builder.setText(spannedText);
}
 
Example 16
Source Project: doov   File: AstHtmlRenderer.java    License: Apache License 2.0 4 votes vote down vote up
private void toHtml(Metadata metadata, ArrayDeque<Metadata> parents) {
        parents.push(metadata);
        try {
            switch (metadata.type()) {
                case RULE:
                    rule(metadata, parents);
                    break;
                case WHEN:
                    when(metadata, parents);
                    break;
                case BINARY_PREDICATE:
                case TEMPLATE_PARAM:
                    binary(metadata, parents);
                    break;
                case LEAF_PREDICATE:
                case FIELD_PREDICATE:
                case LEAF_VALUE:
                case MAPPING_LEAF:
                case TEMPLATE_IDENTIFIER:
                    leaf(metadata, parents);
                    break;
                case MAPPING_LEAF_ITERABLE:
                    iterable(metadata, parents);
                    break;
                case TYPE_CONVERTER:
                    typeConverter(metadata, parents);
                    break;
                case UNARY_PREDICATE:
                    unary(metadata, parents);
                    break;
                case NARY_PREDICATE:
                case MULTIPLE_MAPPING:
                case THEN_MAPPING:
                case ELSE_MAPPING:
                    nary(metadata, parents);
                    break;
                case MAPPING_INPUT:
                    mappingInput(metadata, parents);
                    break;
                case FIELD_PREDICATE_MATCH_ANY:
                    iterable(metadata, parents);
//                    fieldMatchAny(metadata, parents);
                    break;
                case SINGLE_MAPPING:
                    singleMapping(metadata, parents);
                    break;
                default:
                    throw new IllegalStateException(metadata.type().name());
            }
        } finally {
            parents.pop();
        }
    }
 
Example 17
Source Project: dfalex   File: DfaAuxiliaryInformation.java    License: Apache License 2.0 4 votes vote down vote up
/**
 * Perform a depth first search of all states, starting at the start states
 * <P>
 * To avoid stack overflow errors on large DFAs, the implementation uses an auxiliary
 * stack on the heap instead of recursing
 * 
 * @param onEnter  called with (parent, child) when a child is entered.  parent == null for roots.
 * @param onSkip  called with (parent, child) when a child is skipped because it has been entered
 *                  previously.  parent == null for roots.
 * @param onLeave  called with (parent, child) when a child is exited.  parent == null for roots.
 */
public void depthFirstSearch(
        BiConsumer<DfaState<MATCHRESULT>, DfaState<MATCHRESULT>> onEnter,
        BiConsumer<DfaState<MATCHRESULT>, DfaState<MATCHRESULT>> onSkip,
        BiConsumer<DfaState<MATCHRESULT>, DfaState<MATCHRESULT>> onLeave)
{
    @SuppressWarnings("unchecked")
    final Iterator<DfaState<MATCHRESULT>>[] iterators = 
        (Iterator<DfaState<MATCHRESULT>>[]) new Iterator<?>[getStatesByNumber().size()];
    final ArrayDeque<DfaState<MATCHRESULT>> stack = new ArrayDeque<>();
    for (int rootIndex = 0; rootIndex < m_startStates.size(); ++rootIndex)
    {
        DfaState<MATCHRESULT> st = m_startStates.get(rootIndex);
        if (iterators[st.getStateNumber()] != null)
        {
            onSkip.accept(null, st);
            continue;
        }
        iterators[st.getStateNumber()] = st.getSuccessorStates().iterator();
        stack.push(st);
        onEnter.accept(null, st);
        for (;;)
        {
            //process the next child of the stack top
            st = stack.peek();
            final int sti = st.getStateNumber();
            final Iterator<DfaState<MATCHRESULT>> iter = iterators[sti];
            if (iter.hasNext())
            {
                final DfaState<MATCHRESULT> child = iter.next();
                if (child == null)
                {
                    //shouldn't happen, but if it does get the next child
                    continue;
                }
                final int childi = child.getStateNumber();
                if (iterators[childi] != null)
                {
                    onSkip.accept(st, child);
                }
                else
                {
                    iterators[childi] = child.getSuccessorStates().iterator();
                    stack.push(child);
                    onEnter.accept(st, child);
                }
            }
            else
            {
                //top element is done
                stack.pop();
                if (stack.isEmpty())
                {
                    onLeave.accept(null, st);
                    break;
                }
                onLeave.accept(stack.peek(), st);
            }
        }
    }
}
 
Example 18
Source Project: JWebAssembly   File: BranchManger.java    License: Apache License 2.0 4 votes vote down vote up
/**
 * Calculate the block type. The value type that is on the stack after the block.
 * 
 * @param instructions
 *            the instructions of the function
 */
void calculateBlockType( List<WasmInstruction> instructions ) {
    for( int i = size() - 1; i >= 0; i-- ) {
        BranchNode branch = get( i );
        branch.calculateBlockType( instructions );
    }

    if( startBlock != null && startBlock.getOperation() == WasmBlockOperator.IF ) {
        try {
            ArrayDeque<AnyType> stack = new ArrayDeque<>();
            stack.push( ValueType.empty );
            INSTRUCTIONS: for( int i = startIdx; i < instructions.size(); i++ ) {
                WasmInstruction instr = instructions.get( i );
                int codePos = instr.getCodePosition();
                if( codePos >= endPos ) {
                    break;
                }
                int popCount = instr.getPopCount();
                for( int p = 0; p < popCount; p++ ) {
                    stack.pop();
                }
                AnyType pushValue = instr.getPushValueType();
                if( pushValue != null ) {
                    stack.push( pushValue );
                }

                if( instr.getType() == Type.Block ) {
                    switch( ((WasmBlockInstruction)instr).getOperation() ) {
                        case RETURN:
                            // set "empty" block type
                            while( stack.size() > 1 ) {
                                stack.pop();
                            }
                            break INSTRUCTIONS;
                        case IF:
                        case BLOCK:
                        case LOOP:
                        case TRY:
                            // skip the content of the block, important to not count ELSE blocks
                            i = findEndInstruction( instructions, i );
                            break;
                    }
                }
            }
            startBlock.setData( stack.pop() );
        } catch( Throwable th ) {
            throw WasmException.create( th, startBlock.getLineNumber() );
        }
    }
}
 
Example 19
Source Project: ignite   File: TxDeadlockDetection.java    License: Apache License 2.0 4 votes vote down vote up
/**
 * @param wfg Wait-for-graph.
 * @param txId Tx ID - start vertex for cycle search in graph.
 */
static List<GridCacheVersion> findCycle(Map<GridCacheVersion, Set<GridCacheVersion>> wfg, GridCacheVersion txId) {
    if (wfg == null || wfg.isEmpty())
        return null;

    ArrayDeque<GridCacheVersion> stack = new ArrayDeque<>();
    Set<GridCacheVersion> inPath = new HashSet<>();
    Set<GridCacheVersion> visited = new HashSet<>();
    Map<GridCacheVersion, GridCacheVersion> edgeTo = new HashMap<>();

    stack.push(txId);

    while (!stack.isEmpty()) {
        GridCacheVersion v = stack.peek();

        if (visited.contains(v)) {
            stack.pop();
            inPath.remove(v);

            continue;
        }

        visited.add(v);

        Set<GridCacheVersion> children = wfg.get(v);

        if (children == null || children.isEmpty()) {
            stack.pop();
            inPath.remove(v);

            continue;
        }

        inPath.add(v);

        for (GridCacheVersion w : children) {
            if (inPath.contains(w) && visited.contains(w)) {
                List<GridCacheVersion> cycle = new ArrayList<>();

                for (GridCacheVersion x = v; !x.equals(w); x = edgeTo.get(x))
                    cycle.add(x);

                cycle.add(w);
                cycle.add(v);

                return cycle;
            }

            edgeTo.put(w, v);
            stack.push(w);
        }
    }

    return null;
}
 
Example 20
/**
 * Parses the text payload of a WebVTT Cue and applies modifications on {@link WebvttCue.Builder}.
 *
 * @param id Id of the cue, {@code null} if it is not present.
 * @param markup The markup text to be parsed.
 * @param styles List of styles defined by the CSS style blocks preceeding the cues.
 * @param builder Output builder.
 */
/* package */ static void parseCueText(String id, String markup, WebvttCue.Builder builder,
    List<WebvttCssStyle> styles) {
  SpannableStringBuilder spannedText = new SpannableStringBuilder();
  ArrayDeque<StartTag> startTagStack = new ArrayDeque<>();
  List<StyleMatch> scratchStyleMatches = new ArrayList<>();
  int pos = 0;
  while (pos < markup.length()) {
    char curr = markup.charAt(pos);
    switch (curr) {
      case CHAR_LESS_THAN:
        if (pos + 1 >= markup.length()) {
          pos++;
          break; // avoid ArrayOutOfBoundsException
        }
        int ltPos = pos;
        boolean isClosingTag = markup.charAt(ltPos + 1) == CHAR_SLASH;
        pos = findEndOfTag(markup, ltPos + 1);
        boolean isVoidTag = markup.charAt(pos - 2) == CHAR_SLASH;
        String fullTagExpression = markup.substring(ltPos + (isClosingTag ? 2 : 1),
            isVoidTag ? pos - 2 : pos - 1);
        String tagName = getTagName(fullTagExpression);
        if (tagName == null || !isSupportedTag(tagName)) {
          continue;
        }
        if (isClosingTag) {
          StartTag startTag;
          do {
            if (startTagStack.isEmpty()) {
              break;
            }
            startTag = startTagStack.pop();
            applySpansForTag(id, startTag, spannedText, styles, scratchStyleMatches);
          } while(!startTag.name.equals(tagName));
        } else if (!isVoidTag) {
          startTagStack.push(StartTag.buildStartTag(fullTagExpression, spannedText.length()));
        }
        break;
      case CHAR_AMPERSAND:
        int semiColonEndIndex = markup.indexOf(CHAR_SEMI_COLON, pos + 1);
        int spaceEndIndex = markup.indexOf(CHAR_SPACE, pos + 1);
        int entityEndIndex = semiColonEndIndex == -1 ? spaceEndIndex
            : (spaceEndIndex == -1 ? semiColonEndIndex
                : Math.min(semiColonEndIndex, spaceEndIndex));
        if (entityEndIndex != -1) {
          applyEntity(markup.substring(pos + 1, entityEndIndex), spannedText);
          if (entityEndIndex == spaceEndIndex) {
            spannedText.append(" ");
          }
          pos = entityEndIndex + 1;
        } else {
          spannedText.append(curr);
          pos++;
        }
        break;
      default:
        spannedText.append(curr);
        pos++;
        break;
    }
  }
  // apply unclosed tags
  while (!startTagStack.isEmpty()) {
    applySpansForTag(id, startTagStack.pop(), spannedText, styles, scratchStyleMatches);
  }
  applySpansForTag(id, StartTag.buildWholeCueVirtualTag(), spannedText, styles,
      scratchStyleMatches);
  builder.setText(spannedText);
}