private void processComponentStart( String tagName, String jwcId, boolean emptyTag, int startLine, int cursorStart, ILocation startLocation) throws TemplateParseException { if (jwcId.equalsIgnoreCase(CONTENT_ID)) { processContentTag(tagName, startLine, cursorStart, emptyTag); return; } boolean isRemoveId = jwcId.equalsIgnoreCase(REMOVE_ID); if (_ignoring && !isRemoveId) templateParseProblem( Tapestry.format( "TemplateParser.component-may-not-be-ignored", tagName, Integer.toString(startLine)), startLocation, startLine, cursorStart); String type = null; boolean allowBody = false; if (_patternMatcher.matches(jwcId, _implicitIdPattern)) { MatchResult match = _patternMatcher.getMatch(); jwcId = match.group(IMPLICIT_ID_PATTERN_ID_GROUP); type = match.group(IMPLICIT_ID_PATTERN_TYPE_GROUP); String libraryId = match.group(IMPLICIT_ID_PATTERN_LIBRARY_ID_GROUP); String simpleType = match.group(IMPLICIT_ID_PATTERN_SIMPLE_TYPE_GROUP); // If (and this is typical) no actual component id was specified, // then generate one on the fly. // The allocated id for anonymous components is // based on the simple (unprefixed) type, but starts // with a leading dollar sign to ensure no conflicts // with user defined component ids (which don't allow dollar signs // in the id). if (jwcId == null) jwcId = _idAllocator.allocateId("$" + simpleType); try { allowBody = _delegate.getAllowBody(libraryId, simpleType, startLocation); } catch (ApplicationRuntimeException e) { // give subclasses a chance to handle and rethrow templateParseProblem(e, startLine, cursorStart); } } else { if (!isRemoveId) { if (!_patternMatcher.matches(jwcId, _simpleIdPattern)) templateParseProblem( Tapestry.format( "TemplateParser.component-id-invalid", tagName, Integer.toString(startLine), jwcId), startLocation, startLine, cursorStart); if (!_delegate.getKnownComponent(jwcId)) templateParseProblem( Tapestry.format( "TemplateParser.unknown-component-id", tagName, Integer.toString(startLine), jwcId), startLocation, startLine, cursorStart); try { allowBody = _delegate.getAllowBody(jwcId, startLocation); } catch (ApplicationRuntimeException e) { // give subclasses a chance to handle and rethrow templateParseProblem(e, startLine, cursorStart); } } } // Ignore the body if we're removing the entire tag, // of if the corresponding component doesn't allow // a body. boolean ignoreBody = !emptyTag && (isRemoveId || !allowBody); if (_ignoring && ignoreBody) templateParseProblem( Tapestry.format("TemplateParser.nested-ignore", tagName, Integer.toString(startLine)), new Location(_resourceLocation, startLine), startLine, cursorStart); if (!emptyTag) pushNewTag(tagName, startLine, isRemoveId, ignoreBody); // End any open block. addTextToken(cursorStart - 1); if (!isRemoveId) { addOpenToken(tagName, jwcId, type, startLocation); if (emptyTag) _tokens.add(_factory.createCloseToken(tagName, getCurrentLocation())); } advance(); }
/** * Invoked to handle a closing tag, i.e., </foo>. When a tag closes, it will match against a * tag on the open tag start. Preferably the top tag on the stack (if everything is well * balanced), but this is HTML, not XML, so many tags won't balance. * * <p>Once the matching tag is located, the question is ... is the tag dynamic or static? If * static, then the current text block is extended to include this close tag. If dynamic, then the * current text block is ended (before the '<' that starts the tag) and a close token is added. * * <p>In either case, the matching static element and anything above it is removed, and the cursor * is left on the character following the '>'. */ private void closeTag() throws TemplateParseException { int cursorStart = _cursor; int length = _templateData.length; int startLine = _line; ILocation startLocation = getCurrentLocation(); _cursor += CLOSE_TAG.length; int tagStart = _cursor; while (true) { if (_cursor >= length) templateParseProblem( Tapestry.format("TemplateParser.incomplete-close-tag", Integer.toString(startLine)), startLocation, startLine, cursorStart); char ch = _templateData[_cursor]; if (ch == '>') break; advance(); } String tagName = new String(_templateData, tagStart, _cursor - tagStart); int stackPos = _stack.size() - 1; Tag tag = null; while (stackPos >= 0) { tag = (Tag) _stack.get(stackPos); if (tag.match(tagName)) break; if (tag._mustBalance) templateParseProblem( Tapestry.format( "TemplateParser.improperly-nested-close-tag", new Object[] { tagName, Integer.toString(startLine), tag._tagName, Integer.toString(tag._line) }), startLocation, startLine, cursorStart); stackPos--; } // test begin add is page condiction if (stackPos < 0) { if (_delegate.getComponent().getSpecification().isPageSpecification()) { templateParseProblem( Tapestry.format( "TemplateParser.unmatched-close-tag", tagName, Integer.toString(startLine)), startLocation, startLine, cursorStart); } } // test end // Special case for the content tag // test begin if (tag != null) { if (tag._content) { addTextToken(cursorStart - 1); // Advance the cursor right to the end. _cursor = length; _stack.clear(); return; } // When a component closes, add a CLOSE tag. if (tag._component) { addTextToken(cursorStart - 1); _tokens.add(_factory.createCloseToken(tagName, getCurrentLocation())); } else { // The close of a static tag. Unless removing the tag // entirely, make sure the block tag is part of a text block. if (_blockStart < 0 && !tag._removeTag && !_ignoring) _blockStart = cursorStart; } // Remove all elements at stackPos or above. // test begin add && i >= 0 for (int i = _stack.size() - 1; i >= stackPos && i >= 0; i--) _stack.remove(i); // test end // Advance cursor past '>' advance(); // If editting out the tag (i.e., $remove$) then kill any whitespace. // For components that simply don't contain a body, removeTag will // be false. if (tag._removeTag) advanceOverWhitespace(); // If we were ignoring the body of the tag, then clear the ignoring // flag, since we're out of the body. if (tag._ignoringBody) _ignoring = false; } // test end }