private void startTag()

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();
    }