in tapestry-framework/src/java/org/apache/tapestry/parse/TemplateParser.java [581:885]
private void startTag() throws TemplateParseException
{
int cursorStart = _cursor;
int length = _templateData.length;
String tagName = null;
boolean endOfTag = false;
boolean emptyTag = false;
int startLine = _line;
Location startLocation = new LocationImpl(_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 message = (tagName == null) ? ParseMessages.unclosedUnknownTag(startLine)
: ParseMessages.unclosedTag(tagName, startLine);
templateParseProblem(message, 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(ParseMessages.missingAttributeValue(
tagName,
_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);
addAttributeIfUnique(tagName, 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);
addAttributeIfUnique(tagName, 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(_componentAttributeName, _attributes);
if (localizationKey != null && jwcId == null)
{
if (_ignoring)
templateParseProblem(
ParseMessages.componentMayNotBeIgnored(tagName, 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 = false;
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.
advance();
// TAPESTRY-359: *don't* skip whitespace 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();
}