private void checkAndCopyAttributes(Attributes attributes, ElementInfo elInfo)
      throws SAXException, RDFParseException {
    Atts atts = new Atts(attributes.getLength());

    int attCount = attributes.getLength();
    for (int i = 0; i < attCount; i++) {
      String qName = attributes.getQName(i);
      String value = attributes.getValue(i);

      // attributes starting with "xml" should be ignored, except for the
      // ones that are handled by this parser (xml:lang and xml:base).
      if (qName.startsWith("xml")) {
        if (qName.equals("xml:lang")) {
          elInfo.xmlLang = value;
        } else if (qName.equals("xml:base")) {
          elInfo.setBaseURI(value);
        }
      } else {
        String namespace = attributes.getURI(i);
        String localName = attributes.getLocalName(i);

        // A limited set of unqualified attributes must be supported by
        // parsers, as is specified in section 6.1.4 of the spec
        if ("".equals(namespace)) {
          if (localName.equals("ID")
              || localName.equals("about")
              || localName.equals("resource")
              || localName.equals("parseType")
              || localName.equals("type")) {
            rdfParser.reportWarning(
                "use of unqualified attribute " + localName + " has been deprecated");
            namespace = RDF.NAMESPACE;
          }
        }

        if ("".equals(namespace)) {
          rdfParser.reportError(
              "unqualified attribute '" + qName + "' not allowed",
              XMLParserSettings.FAIL_ON_INVALID_QNAME);
        }

        Att att = new Att(namespace, localName, qName, value);
        atts.addAtt(att);
      }
    }

    elInfo.atts = atts;
  }
  public void startElement(
      String namespaceURI, String localName, String qName, Attributes attributes)
      throws SAXException {
    try {
      if (deferredElement != null) {
        // The next call could set parseLiteralMode to true!
        reportDeferredStartElement();
      }

      if (parseLiteralMode) {
        appendStartTag(qName, attributes);
        xmlLiteralStackHeight++;
      } else {
        ElementInfo parent = peekStack();
        ElementInfo elInfo = new ElementInfo(parent, qName, namespaceURI, localName);

        elInfo.setNamespaceMappings(newNamespaceMappings);
        newNamespaceMappings.clear();

        if (!inRDFContext
            && parseStandAloneDocuments
            && (!localName.equals("RDF") || !namespaceURI.equals(RDF.NAMESPACE))) {
          // Stand-alone document that does not start with an rdf:RDF root
          // element. Assume this root element is omitted.
          inRDFContext = true;
        }

        if (!inRDFContext) {
          // Check for presence of xml:base and xlm:lang attributes.
          for (int i = 0; i < attributes.getLength(); i++) {
            String attQName = attributes.getQName(i);

            if ("xml:base".equals(attQName)) {
              elInfo.setBaseURI(attributes.getValue(i));
            } else if ("xml:lang".equals(attQName)) {
              elInfo.xmlLang = attributes.getValue(i);
            }
          }

          elInfoStack.push(elInfo);

          // Check if we are entering RDF context now.
          if (localName.equals("RDF") && namespaceURI.equals(RDF.NAMESPACE)) {
            inRDFContext = true;
            rdfContextStackHeight = 0;
          }
        } else {
          // We're parsing RDF elements.
          checkAndCopyAttributes(attributes, elInfo);

          // Don't report the new element to the RDF parser just yet.
          deferredElement = elInfo;
        }

        charBuf.setLength(0);
      }
    } catch (RDFParseException e) {
      throw new SAXException(e);
    } catch (RDFHandlerException e) {
      throw new SAXException(e);
    }
  }