Package com.google.caja.parser.html

Source Code of com.google.caja.parser.html.XmlElementStack

// Copyright (C) 2007 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.caja.parser.html;

import com.google.caja.lexer.FilePosition;
import com.google.caja.lexer.HtmlTokenType;
import com.google.caja.lexer.InputSource;
import com.google.caja.lexer.Token;
import com.google.caja.reporting.Message;
import com.google.caja.reporting.MessagePart;
import com.google.caja.reporting.MessageQueue;
import com.google.caja.reporting.MessageType;

import java.util.Iterator;
import java.util.List;

import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

/**
* An element stack implementation for XML.
*
* @author mikesamuel@gmail.com
*/
class XmlElementStack extends AbstractElementStack {
  private final MessageQueue mq;

  XmlElementStack(
      Document doc, Namespaces ns, boolean needsDebugData, MessageQueue mq) {
    super(doc, ns, needsDebugData);
    this.mq = mq;
  }

  public boolean needsNamespaceFixup() { return false; }

  /** {@inheritDoc} */
  public void processTag(Token<HtmlTokenType> start, Token<HtmlTokenType> end,
                         List<AttrStub> attrs)
      throws IllegalDocumentStateException {
    assert start.type == HtmlTokenType.TAGBEGIN;
    assert end.type == HtmlTokenType.TAGEND;

    boolean open = !start.text.startsWith("</");
    processTag(start.text.substring(open ? 1 : 2), open, start, end, attrs);
  }

  private void processTag(
      String elQName, boolean open, Token<HtmlTokenType> start,
      Token<HtmlTokenType> end, List<AttrStub> attrs)
      throws IllegalDocumentStateException {
    if (open) {
      OpenNode bottom = getBottom();
      Namespaces ns = bottom.ns;
      for (Iterator<AttrStub> it = attrs.iterator(); it.hasNext();) {
        AttrStub a = it.next();
        Namespaces fromAttr = a.toNamespace(ns, mq);
        if (fromAttr != null) {
          ns = fromAttr;
          it.remove();
        }
      }

      Namespaces elNs = ns.forElementName(elQName);
      if (elNs == null) {
        elNs = ns = unknownNamespace(start.pos, ns, elQName, mq);
      }
      Element newElement = doc.createElementNS(elNs.uri, elQName);
      for (AttrStub a : attrs) {
        String attrQName = a.nameTok.text;
        Namespaces attrNs = ns.forAttrName(elNs, attrQName);
        if (attrNs == null) {
          attrNs = ns = unknownNamespace(a.nameTok.pos, ns, attrQName, mq);
        }
        String localAttrName = Namespaces.localName(attrNs.uri, attrQName);
        if (!newElement.hasAttributeNS(attrNs.uri, localAttrName)) {
          if (needsDebugData) {
            Attr attrNode = a.toAttr(doc, attrNs.uri, attrQName);
            newElement.setAttributeNodeNS(attrNode);
          } else {
            newElement.setAttributeNS(attrNs.uri, attrQName, a.value);
          }
        } else {
          mq.addMessage(
              MessageType.DUPLICATE_ATTRIBUTE, a.nameTok.pos,
              MessagePart.Factory.valueOf(attrQName),
              Nodes.getFilePositionFor(
                  newElement.getAttributeNodeNS(attrNs.uri, localAttrName)));
        }
      }
      if (needsDebugData) {
        Nodes.setFilePositionFor(
            newElement, FilePosition.span(start.pos, end.pos));
      }
      // Does the tag end immediately?
      if ("/>".equals(end.text)) {
        doAppend(newElement, bottom.n);
      } else {
        push(newElement, ns, elQName);
      }
    } else {
      String bottomElementName = getBottom().qname;
      if (!elQName.equals(bottomElementName)) {
        throw new IllegalDocumentStateException(new Message(
            DomParserMessageType.UNMATCHED_END,
            start.pos, MessagePart.Factory.valueOf(start.text),
            MessagePart.Factory.valueOf("<" + bottomElementName)));
      }
      popN(1, end.pos);
    }
  }

  /** {@inheritDoc} */
  public void processText(Token<HtmlTokenType> text) {
    Node parent = getBottom().n;

    Text textNode;
    switch (text.type) {
      case CDATA:
        textNode = doc.createCDATASection(
            text.text.substring(9, text.text.length() - 3));
        break;
      case TEXT:
        {
          Node lastSibling = parent.getLastChild();
          if (lastSibling != null) {
            if (lastSibling.getNodeType() == Node.TEXT_NODE) {
              Text combined = doc.createTextNode(
                  lastSibling.getNodeValue() + Nodes.decode(text.text));
              if (needsDebugData) {
                Nodes.setRawText(
                    combined, Nodes.getRawText((Text) lastSibling) + text.text);
                Nodes.setFilePositionFor(
                    combined,
                    FilePosition.span(
                        Nodes.getFilePositionFor(lastSibling), text.pos));
              }
              parent.replaceChild(combined, lastSibling);
              return;
            }
          }
        }
        textNode = doc.createTextNode(Nodes.decode(text.text));
        break;
      case UNESCAPED:
        textNode = doc.createTextNode(text.text);
        break;
      default:
        throw new IllegalArgumentException(text.toString());
    }
    if (needsDebugData) {
      Nodes.setRawText(textNode, text.text);
      Nodes.setFilePositionFor(textNode, text.pos);
    }
    doAppend(textNode, parent);
  }

  /**
   * Adds the given comment node to the DOM.
   */
  public void processComment(Token<HtmlTokenType> commentToken) {
    String text = commentToken.text.substring("<!--".length(),
        commentToken.text.lastIndexOf("--"));
    Comment comment = doc.createComment(text);
    comment.setUserData("COMMENT_TYPE", commentToken.type.toString() , null);
    if (needsDebugData) {
      Nodes.setFilePositionFor(comment, commentToken.pos);
    }
    doAppend(comment, getBottom().n);
  }

  /** {@inheritDoc} */
  public void finish(FilePosition endOfDocument)
      throws IllegalDocumentStateException {
    stripIgnorableText();
    DocumentFragment root = getRootElement();

    if (needsDebugData) {
      FilePosition rootStart = Nodes.getFilePositionFor(root);
      if (rootStart == null || InputSource.UNKNOWN.equals(rootStart.source())) {
        if (root.getFirstChild() == null) {
          rootStart = endOfDocument;
        } else {
          rootStart = Nodes.getFilePositionFor(root.getFirstChild());
        }
      }
      if (rootStart.startCharInFile() <= endOfDocument.startCharInFile()) {
        Nodes.setFilePositionFor(
            root, FilePosition.span(rootStart, endOfDocument));
      }
    }

    int nOpen = getNOpenElements();
    if (nOpen != 1) {
      Element openEl = getElement(nOpen - 1);
      throw new IllegalDocumentStateException(new Message(
          DomParserMessageType.MISSING_END, endOfDocument,
          MessagePart.Factory.valueOf(openEl.getTagName()),
          Nodes.getFilePositionFor(openEl)));
    }
  }

  /** We do no entity fixup in XML mode. */
  public String fixBrokenEntities(String rawText, FilePosition textPos) {
    return rawText;
  }
}
TOP

Related Classes of com.google.caja.parser.html.XmlElementStack

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.