private void pushNewTag(String tagName, int startLine, boolean isRemoveId, boolean ignoreBody) { Tag tag = new Tag(tagName, startLine); tag._component = !isRemoveId; tag._removeTag = isRemoveId; tag._ignoringBody = ignoreBody; _ignoring = tag._ignoringBody; tag._mustBalance = true; _stack.add(tag); }
private void startTag() throws TemplateParseException { int cursorStart = _cursor; int length = _templateData.length; String tagName = null; boolean endOfTag = false; boolean emptyTag = false; int startLine = _line; ILocation startLocation = new Location(_resourceLocation, startLine); tagBeginEvent(startLine, _cursor); advance(); // Collect the element type while (_cursor < length) { char ch = _templateData[_cursor]; if (ch == '/' || ch == '>' || Character.isWhitespace(ch)) { tagName = new String(_templateData, cursorStart + 1, _cursor - cursorStart - 1); break; } advance(); } String attributeName = null; int attributeNameStart = -1; int attributeValueStart = -1; int state = WAIT_FOR_ATTRIBUTE_NAME; char quoteChar = 0; _attributes.clear(); // Collect each attribute while (!endOfTag) { if (_cursor >= length) { String key = (tagName == null) ? "TemplateParser.unclosed-unknown-tag" : "TemplateParser.unclosed-tag"; templateParseProblem( Tapestry.format(key, tagName, Integer.toString(startLine)), startLocation, startLine, cursorStart); } char ch = _templateData[_cursor]; switch (state) { case WAIT_FOR_ATTRIBUTE_NAME: // Ignore whitespace before the next attribute name, while // looking for the end of the current tag. if (ch == '/') { emptyTag = true; advance(); break; } if (ch == '>') { endOfTag = true; break; } if (Character.isWhitespace(ch)) { advance(); break; } // Found non-whitespace, assume its the attribute name. // Note: could use a check here for non-alpha. attributeNameStart = _cursor; state = COLLECT_ATTRIBUTE_NAME; advance(); break; case COLLECT_ATTRIBUTE_NAME: // Looking for end of attribute name. if (ch == '=' || ch == '/' || ch == '>' || Character.isWhitespace(ch)) { attributeName = new String(_templateData, attributeNameStart, _cursor - attributeNameStart); state = ADVANCE_PAST_EQUALS; break; } // Part of the attribute name advance(); break; case ADVANCE_PAST_EQUALS: // Looking for the '=' sign. May hit the end of the tag, or (for bare attributes), // the next attribute name. if (ch == '/' || ch == '>') { // A bare attribute, which is not interesting to // us. state = WAIT_FOR_ATTRIBUTE_NAME; break; } if (Character.isWhitespace(ch)) { advance(); break; } if (ch == '=') { state = WAIT_FOR_ATTRIBUTE_VALUE; quoteChar = 0; attributeValueStart = -1; advance(); break; } // Otherwise, an HTML style "bare" attribute (such as <select multiple>). // We aren't interested in those (we're just looking for the id or jwcid attribute). state = WAIT_FOR_ATTRIBUTE_NAME; break; case WAIT_FOR_ATTRIBUTE_VALUE: if (ch == '/' || ch == '>') templateParseProblem( Tapestry.format( "TemplateParser.missing-attribute-value", tagName, Integer.toString(_line), attributeName), getCurrentLocation(), _line, _cursor); // Ignore whitespace between '=' and the attribute value. Also, look // for initial quote. if (Character.isWhitespace(ch)) { advance(); break; } if (ch == '\'' || ch == '"') { quoteChar = ch; state = COLLECT_QUOTED_VALUE; advance(); attributeValueStart = _cursor; attributeBeginEvent(attributeName, _line, attributeValueStart); break; } // Not whitespace or quote, must be start of unquoted attribute. state = COLLECT_UNQUOTED_VALUE; attributeValueStart = _cursor; attributeBeginEvent(attributeName, _line, attributeValueStart); break; case COLLECT_QUOTED_VALUE: // Start collecting the quoted attribute value. Stop at the matching quote character, // unless bare, in which case, stop at the next whitespace. if (ch == quoteChar) { String attributeValue = new String(_templateData, attributeValueStart, _cursor - attributeValueStart); attributeEndEvent(_cursor); if (_attributes.containsKey(attributeName)) templateParseProblem( Tapestry.format( "TemplateParser.duplicate-tag-attribute", tagName, Integer.toString(_line), attributeName), getCurrentLocation(), _line, _cursor); _attributes.put(attributeName, attributeValue); // Advance over the quote. advance(); state = WAIT_FOR_ATTRIBUTE_NAME; break; } advance(); break; case COLLECT_UNQUOTED_VALUE: // An unquoted attribute value ends with whitespace // or the end of the enclosing tag. if (ch == '/' || ch == '>' || Character.isWhitespace(ch)) { String attributeValue = new String(_templateData, attributeValueStart, _cursor - attributeValueStart); attributeEndEvent(_cursor); if (_attributes.containsKey(attributeName)) templateParseProblem( Tapestry.format( "TemplateParser.duplicate-tag-attribute", tagName, Integer.toString(_line), attributeName), getCurrentLocation(), _line, _cursor); _attributes.put(attributeName, attributeValue); state = WAIT_FOR_ATTRIBUTE_NAME; break; } advance(); break; } } tagEndEvent(_cursor); // Check for invisible localizations String localizationKey = findValueCaselessly(LOCALIZATION_KEY_ATTRIBUTE_NAME, _attributes); String jwcId = findValueCaselessly(JWCID_ATTRIBUTE_NAME, _attributes); if (localizationKey != null && tagName.equalsIgnoreCase("span") && jwcId == null) { if (_ignoring) templateParseProblem( Tapestry.format( "TemplateParser.component-may-not-be-ignored", tagName, Integer.toString(startLine)), startLocation, startLine, cursorStart); // If the tag isn't empty, then create a Tag instance to ignore the // body of the tag. if (!emptyTag) { Tag tag = new Tag(tagName, startLine); tag._component = false; tag._removeTag = true; tag._ignoringBody = true; tag._mustBalance = true; _stack.add(tag); // Start ignoring content until the close tag. _ignoring = true; } else { // Cursor is at the closing carat, advance over it and any whitespace. advance(); advanceOverWhitespace(); } // End any open block. addTextToken(cursorStart - 1); boolean raw = checkBoolean(RAW_ATTRIBUTE_NAME, _attributes); Map attributes = filter(_attributes, new String[] {LOCALIZATION_KEY_ATTRIBUTE_NAME, RAW_ATTRIBUTE_NAME}); TemplateToken token = _factory.createLocalizationToken( tagName, localizationKey, raw, attributes, startLocation); _tokens.add(token); return; } if (jwcId != null) { processComponentStart(tagName, jwcId, emptyTag, startLine, cursorStart, startLocation); return; } // A static tag (not a tag without a jwcid attribute). // We need to record this so that we can match close tags later. if (!emptyTag) { Tag tag = new Tag(tagName, startLine); _stack.add(tag); } // If there wasn't an active block, then start one. if (_blockStart < 0 && !_ignoring) _blockStart = cursorStart; advance(); }