in tapestry-framework/src/org/apache/tapestry/parse/TemplateParser.java [660:991]
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();
}