protected char parseAttribute()

in core/camel-xml-io/src/main/java/org/apache/camel/xml/io/MXParser.java [1849:2098]


    protected char parseAttribute() throws XmlPullParserException, IOException {
        // parse attribute
        // [41] Attribute ::= Name Eq AttValue
        // [WFC: No External Entity References]
        // [WFC: No < in Attribute Values]
        final int prevPosStart = posStart + bufAbsoluteStart;
        final int nameStart = pos - 1 + bufAbsoluteStart;
        int colonPos = -1;
        char ch = buf[pos - 1];
        if (ch == ':' && processNamespaces)
            throw new XmlPullParserException(
                    "when namespaces processing enabled colon can not be at attribute name start", this, null);

        boolean startsWithXmlns = processNamespaces && ch == 'x';
        int xmlnsPos = 0;

        ch = more();
        while (isNameChar(ch)) {
            if (processNamespaces) {
                if (startsWithXmlns && xmlnsPos < 5) {
                    ++xmlnsPos;
                    if (xmlnsPos == 1) {
                        if (ch != 'm')
                            startsWithXmlns = false;
                    } else if (xmlnsPos == 2) {
                        if (ch != 'l')
                            startsWithXmlns = false;
                    } else if (xmlnsPos == 3) {
                        if (ch != 'n')
                            startsWithXmlns = false;
                    } else if (xmlnsPos == 4) {
                        if (ch != 's')
                            startsWithXmlns = false;
                    } else {
                        if (ch != ':')
                            throw new XmlPullParserException(
                                    "after xmlns in attribute name must be colon" + " when namespaces are enabled", this, null);
                        // colonPos = pos - 1 + bufAbsoluteStart;
                    }
                }
                if (ch == ':') {
                    if (colonPos != -1)
                        throw new XmlPullParserException(
                                "only one colon is allowed in attribute name" + " when namespaces are enabled", this, null);
                    colonPos = pos - 1 + bufAbsoluteStart;
                }
            }
            ch = more();
        }

        ensureAttributesCapacity(attributeCount);

        // --- start processing attributes
        String name = null;
        String prefix = null;
        // work on prefixes and namespace URI
        if (processNamespaces) {
            if (xmlnsPos < 4)
                startsWithXmlns = false;
            if (startsWithXmlns) {
                if (colonPos != -1) {
                    // prefix = attributePrefix[ attributeCount ] = null;
                    final int nameLen = pos - 2 - (colonPos - bufAbsoluteStart);
                    if (nameLen == 0) {
                        throw new XmlPullParserException(
                                "namespace prefix is required after xmlns: " + " when namespaces are enabled", this, null);
                    }
                    name = // attributeName[ attributeCount ] =
                            newString(buf, colonPos - bufAbsoluteStart + 1, nameLen);
                    // pos - 1 - (colonPos + 1 - bufAbsoluteStart)
                }
            } else {
                if (colonPos != -1) {
                    int prefixLen = colonPos - nameStart;
                    prefix = attributePrefix[attributeCount] = newString(buf, nameStart - bufAbsoluteStart, prefixLen);
                    // colonPos - (nameStart - bufAbsoluteStart));
                    int nameLen = pos - 2 - (colonPos - bufAbsoluteStart);
                    name = attributeName[attributeCount] = newString(buf, colonPos - bufAbsoluteStart + 1, nameLen);
                    // pos - 1 - (colonPos + 1 - bufAbsoluteStart));

                    // name.substring(0, colonPos-nameStart);
                } else {
                    prefix = attributePrefix[attributeCount] = null;
                    name = attributeName[attributeCount]
                            = newString(buf, nameStart - bufAbsoluteStart, pos - 1 - (nameStart - bufAbsoluteStart));
                }
                if (!allStringsInterned) {
                    attributeNameHash[attributeCount] = name.hashCode();
                }
            }

        } else {
            // retrieve name
            name = attributeName[attributeCount]
                    = newString(buf, nameStart - bufAbsoluteStart, pos - 1 - (nameStart - bufAbsoluteStart));
            //// assert name != null;
            if (!allStringsInterned) {
                attributeNameHash[attributeCount] = name.hashCode();
            }
        }

        // [25] Eq ::= S? '=' S?
        while (isS(ch)) {
            ch = more();
        } // skip additional spaces
        if (ch != '=')
            throw new XmlPullParserException("expected = after attribute name", this, null);
        ch = more();
        while (isS(ch)) {
            ch = more();
        } // skip additional spaces

        // [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
        // | "'" ([^<&'] | Reference)* "'"
        final char delimit = ch;
        if (delimit != '"' && delimit != '\'')
            throw new XmlPullParserException(
                    "attribute value must start with quotation or apostrophe not " + printable(delimit), this, null);
        // parse until delimit or < and resolve Reference
        // [67] Reference ::= EntityRef | CharRef
        // int valueStart = pos + bufAbsoluteStart;

        boolean normalizedCR = false;
        usePC = false;
        pcStart = pcEnd;
        posStart = pos;

        while (true) {
            ch = more();
            if (ch == delimit) {
                break;
            }
            if (ch == '<') {
                throw new XmlPullParserException("markup not allowed inside attribute value - illegal < ", this, null);
            }
            if (ch == '&') {
                // extractEntityRef
                posEnd = pos - 1;
                if (!usePC) {
                    final boolean hadCharData = posEnd > posStart;
                    if (hadCharData) {
                        // posEnd is already set correctly!!!
                        joinPC();
                    } else {
                        usePC = true;
                        pcStart = pcEnd = 0;
                    }
                }
                // assert usePC == true;

                final char[] resolvedEntity = parseEntityRef();
                // check if replacement text can be resolved !!!
                if (resolvedEntity == null) {
                    if (entityRefName == null) {
                        entityRefName = newString(buf, posStart, posEnd - posStart);
                    }
                    throw new XmlPullParserException(
                            "could not resolve entity named '" + printable(entityRefName) + "'", this, null);
                }
                // write into PC replacement text - do merge for replacement
                // text!!!!
                for (int i = 0; i < resolvedEntity.length; i++) {
                    if (pcEnd >= pc.length)
                        ensurePC(pcEnd);
                    pc[pcEnd++] = resolvedEntity[i];
                }
            } else if (ch == '\t' || ch == '\n' || ch == '\r') {
                // do attribute value normalization
                // as described in http://www.w3.org/TR/REC-xml#AVNormalize
                // TODO add test for it form spec ...
                // handle EOL normalization ...
                if (!usePC) {
                    posEnd = pos - 1;
                    if (posEnd > posStart) {
                        joinPC();
                    } else {
                        usePC = true;
                        pcEnd = pcStart = 0;
                    }
                }
                // assert usePC == true;
                if (pcEnd >= pc.length)
                    ensurePC(pcEnd);
                if (ch != '\n' || !normalizedCR) {
                    pc[pcEnd++] = ' '; // '\n';
                }

            } else {
                if (usePC) {
                    if (pcEnd >= pc.length)
                        ensurePC(pcEnd);
                    pc[pcEnd++] = ch;
                }
            }
            normalizedCR = ch == '\r';
        }

        if (processNamespaces && startsWithXmlns) {
            String ns;
            if (!usePC) {
                ns = newStringIntern(buf, posStart, pos - 1 - posStart);
            } else {
                ns = newStringIntern(pc, pcStart, pcEnd - pcStart);
            }
            ensureNamespacesCapacity(namespaceEnd);
            int prefixHash = -1;
            if (colonPos != -1) {
                if (ns.isEmpty()) {
                    throw new XmlPullParserException(
                            "non-default namespace can not be declared to be empty string", this, null);
                }
                // declare new namespace
                namespacePrefix[namespaceEnd] = name;
                if (!allStringsInterned) {
                    prefixHash = namespacePrefixHash[namespaceEnd] = name.hashCode();
                }
            } else {
                // declare new default namespace ...
                namespacePrefix[namespaceEnd] = null; // ""; //null; //TODO
                                                     // check FIXME Alek
                if (!allStringsInterned) {
                    prefixHash = namespacePrefixHash[namespaceEnd] = -1;
                }
            }
            namespaceUri[namespaceEnd] = ns;

            // detect duplicate namespace declarations!!!
            final int startNs = elNamespaceCount[depth - 1];
            for (int i = namespaceEnd - 1; i >= startNs; --i) {
                if (((allStringsInterned || name == null) && namespacePrefix[i] == name)
                        || (!allStringsInterned && name != null && namespacePrefixHash[i] == prefixHash
                                && name.equals(namespacePrefix[i]))) {
                    final String s = name == null ? "default" : "'" + name + "'";
                    throw new XmlPullParserException("duplicated namespace declaration for " + s + " prefix", this, null);
                }
            }

            ++namespaceEnd;

        } else {
            if (!usePC) {
                attributeValue[attributeCount] = new String(buf, posStart, pos - 1 - posStart);
            } else {
                attributeValue[attributeCount] = new String(pc, pcStart, pcEnd - pcStart);
            }
            ++attributeCount;
        }
        posStart = prevPosStart - bufAbsoluteStart;
        return ch;
    }