Java tutorial
/* * Copyright 2014 Jordan S. Jones <jordansjones@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package nextmethod.web.razor.parser; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import nextmethod.base.Debug; import nextmethod.base.IDisposable; import nextmethod.base.KeyValue; import nextmethod.base.Strings; import nextmethod.web.razor.parser.syntaxtree.BlockType; import nextmethod.web.razor.parser.syntaxtree.SpanKind; import nextmethod.web.razor.tokenizer.symbols.HtmlSymbol; import nextmethod.web.razor.tokenizer.symbols.HtmlSymbolType; import static nextmethod.web.razor.resources.Mvc4jRazorResources.RazorResources; /** * */ class HtmlMarkupParserSection extends HtmlMarkupParserDelegate { private boolean caseSensitive; public HtmlMarkupParserSection(@Nonnull final HtmlMarkupParser delegate) { super(delegate); } @Override public void parseSection(@Nonnull final KeyValue<String, String> nestingSequence, final boolean caseSensitive) { if (getContext() == null) { throw new UnsupportedOperationException(RazorResources().parserContextNotSet()); } try (IDisposable ignored = pushSpanConfig(defaultMarkupSpanDelegate)) { try (IDisposable ignored2 = getContext().startBlock(BlockType.Markup)) { nextToken(); this.caseSensitive = caseSensitive; if (nestingSequence.getKey() == null) { final Iterable<String> split = Splitter.on(CharMatcher.WHITESPACE) .split(nestingSequence.getValue()); nonNestingSection(Iterables.toArray(split, String.class)); } else { nestingSection(nestingSequence); } addMarkerSymbolIfNecessary(); output(SpanKind.Markup); } } } private void nonNestingSection(@Nonnull final String[] nestingSequenceComponents) { do { skipToAndParseCode(sym -> sym != null && (sym.isType(HtmlSymbolType.OpenAngle) || atEnd(nestingSequenceComponents))); getDocumentParser().scanTagInDocumentContext(); if (!isEndOfFile() && atEnd(nestingSequenceComponents)) { break; } } while (!isEndOfFile()); putCurrentBack(); } private void nestingSection(@Nonnull final KeyValue<String, String> nestingSequence) { int nesting = 1; while (nesting > 0 && !isEndOfFile()) { skipToAndParseCode(sym -> sym != null && sym.isTypeOr(HtmlSymbolType.Text, HtmlSymbolType.OpenAngle)); if (at(HtmlSymbolType.Text)) { nesting += processTextToken(nestingSequence, nesting); if (getCurrentSymbol() != null) { acceptAndMoveNext(); } else if (nesting > 0) { nextToken(); } } else { getDocumentParser().scanTagInDocumentContext(); } } } private boolean atEnd(@Nonnull final String[] nestingSequenceComponents) { ensureCurrent(); if (isEqualTo(getCurrentSymbol().getContent(), nestingSequenceComponents[0])) { final int bookmark = getCurrentSymbol().getStart().getAbsoluteIndex(); try { for (String component : nestingSequenceComponents) { if (!isEqualTo(getCurrentSymbol().getContent(), component)) { return false; } nextToken(); //noinspection ConstantConditions while (!isEndOfFile() && isSpacingToken(true).invoke(getCurrentSymbol())) { nextToken(); } } return true; } finally { getContext().getSource().setPosition(bookmark); nextToken(); } } return false; } private int processTextToken(@Nonnull final KeyValue<String, String> nestingSequence, final int currentNesting) { for (int i = 0, symContentLength = getCurrentSymbol().getContent().length(); i < symContentLength; i++) { int nestingDelta = handleNestingSequence(nestingSequence.getKey(), i, currentNesting, 1); if (nestingDelta == 0) { nestingDelta = handleNestingSequence(nestingSequence.getValue(), i, currentNesting, -1); } if (nestingDelta != 0) { return nestingDelta; } } return 0; } private int handleNestingSequence(@Nullable final String sequence, final int position, final int currentNesting, final int retIfMatched) { if (sequence == null) return 0; final String symContent = getCurrentSymbol().getContent(); if (symContent.charAt(position) == sequence.charAt(0) && (position + sequence.length() <= symContent.length())) { final String possibleStart = symContent.substring(position, (position + sequence.length())); if (isEqualTo(possibleStart, sequence)) { // Capture the current symbol and "put it back" (really we just want to clear CurrentSymbol) final int bookmark = getContext().getSource().getPosition(); HtmlSymbol sym = getCurrentSymbol(); putCurrentBack(); // Carve up the symbol KeyValue<HtmlSymbol, HtmlSymbol> pair = getLanguage().splitSymbol(sym, position, HtmlSymbolType.Text); final HtmlSymbol preSequence = pair.getKey(); if (Debug.isAssertEnabled()) assert pair.getValue() != null; //noinspection ConstantConditions pair = getLanguage().splitSymbol(pair.getValue(), sequence.length(), HtmlSymbolType.Text); final HtmlSymbol sequenceToken = pair.getKey(); final HtmlSymbol postSequence = pair.getValue(); // Accept the first chunk (up to the nesting sequence we just saw) if (preSequence != null && !Strings.isNullOrEmpty(preSequence.getContent())) { accept(preSequence); } // Accept the sequence if it isn't the last one if (currentNesting + retIfMatched != 0) { accept(sequenceToken); int newPosition = bookmark; // Position at the start of the postSequence symbol if (postSequence != null) { newPosition = postSequence.getStart().getAbsoluteIndex(); } getContext().getSource().setPosition(newPosition); } // Return the value we were asked to return if matched, since we found a nesting sequence return retIfMatched; } } return 0; } private boolean isEqualTo(@Nonnull String first, @Nonnull String second) { first = Strings.nullToEmpty(first); second = Strings.nullToEmpty(second); return this.caseSensitive ? first.equals(second) : first.equalsIgnoreCase(second); } }