/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.xml; import com.caucho.util.CharBuffer; import com.caucho.util.L10N; import com.caucho.vfs.ReadStream; import java.util.HashMap; /** * The Policy class defines the parsing policy. It configures the parser * between HTML, XML, and loose versions of HTML and XML. */ class Policy { static L10N L = new L10N(Policy.class); final static int ERROR = 0; final static int IGNORE = ERROR + 1; final static int PUSH = IGNORE + 1; final static int PUSH_EMPTY = PUSH + 1; final static int PUSH_OPT = PUSH_EMPTY + 1; final static int PUSH_VERBATIM = PUSH_OPT + 1; final static int POP = PUSH_VERBATIM + 1; final static int POP_AND_LOOP = POP + 1; private NamespaceMap namespaces; private HashMap nameCache = new HashMap(); private HashMap _attrCache = new HashMap(); protected QName opt; protected ReadStream is; boolean expandReferences = true; boolean optionalTags = true; boolean skipWhitespace; boolean skipComments; boolean strictComments; boolean strictAttributes; boolean entitiesAsText = false; boolean expandEntities = true; boolean strictCharacters; boolean strictXml; boolean singleTopElement; boolean normalizeWhitespace = false; boolean forgiving; boolean _isNamespaceAware = false; /** * Initialize the policy. */ void init() { namespaces = null; nameCache.clear(); _attrCache.clear(); opt = null; is = null; expandReferences = true; optionalTags = true; skipWhitespace = false; skipComments = false; strictComments = false; strictAttributes = false; entitiesAsText = false; expandEntities = true; strictCharacters = false; strictXml = false; singleTopElement = false; normalizeWhitespace = false; forgiving = false; _isNamespaceAware = false; } /** * Sets the current read stream. */ void setStream(ReadStream is) { this.is = is; } QName getOpt() { return opt; } /** * Sets the new namespace binding. * * @param ns the namespace */ void setNamespace(NamespaceMap ns) { if (namespaces != ns) { nameCache.clear(); _attrCache.clear(); } namespaces = ns; } /** * Set true for namespace aware. */ void setNamespaceAware(boolean isNamespaceAware) { _isNamespaceAware = isNamespaceAware; } /** * Clears the namespace cache when the namespace changes. */ void clearNamespaceCache() { namespaces = null; nameCache.clear(); _attrCache.clear(); } QName getAttributeName(CharBuffer eltName, CharBuffer source) { return getAttributeName(eltName, source, false); } /** * Returns the qname for the named attribute. * * @param eltName the current node * @param source the name of the attribute * * @param the QName including namespace for the attribute name. */ QName getAttributeName(CharBuffer eltName, CharBuffer source, boolean nsNull) { QName qname = (QName) _attrCache.get(source); if (qname != null) return qname; int i = source.lastIndexOf(':'); String fullName = source.toString(); String prefix = null; String localName = null; String ns = null; if (! _isNamespaceAware) { localName = fullName; } else if (i < 0) { localName = fullName; } else { prefix = source.substring(0, i); ns = NamespaceMap.get(namespaces, prefix); if (ns != null) { localName = source.substring(i + 1); } else if ("xml".equals(prefix)) { ns = XmlParser.XML; localName = source.substring(i + 1); } else { prefix = null; localName = source.toString(); } } qname = new QName(fullName, prefix, localName, ns); _attrCache.put(source.clone(), qname); return qname; } /** * Returns the fully qualified name, including namespaces, for the * new qname. * * @param node the current parent node * @param source the qname string needing resolving. * * @return the QName including namespace for the source. */ QName getName(CharBuffer source) { QName qname = (QName) nameCache.get(source); if (qname != null) return qname; int i = source.lastIndexOf(':'); String fullName = source.toString(); String prefix = null; String localName = null; String ns = null;; if (! _isNamespaceAware) { } else if (i < 0) { ns = NamespaceMap.get(namespaces, ""); localName = source.toString(); } else { prefix = source.substring(0, i); ns = NamespaceMap.get(namespaces, prefix); if (ns != null) { localName = source.substring(i + 1); } else { prefix = null; localName = source.toString(); } } qname = new QName(fullName, prefix, localName, ns); nameCache.put(source.clone(), qname); return qname; } /** * Returns the fully qualified name, including namespaces, for the * new qname. * * @param source the qname string needing resolving. * * @return the QName including namespace for the source. */ QName getNamespaceName(CharBuffer source) { QName qname = (QName) nameCache.get(source); if (qname != null) return qname; int i = source.lastIndexOf(':'); String prefix; String localName; // xml/01ek if (true) { prefix = null; localName = source.toString(); } else if (i < 0) { prefix = null; localName = source.toString(); } else { prefix = source.substring(0, i); localName = source.substring(i + 1); } // xml/01ek vs xml/01eg qname = new QName(prefix, localName, null); // XmlParser.XMLNS nameCache.put(source.clone(), qname); return qname; } /** * Returns true if the string contains only whitespace. * * @param s string to test * @return true if the string is completely whitespace */ boolean isWhitespaceOnly(String s) { for (int i = s.length() - 1; i >= 0; i--) if (! XmlChar.isWhitespace(s.charAt(i))) return false; return true; } /** * Returns the action to be performed with the next node on an open * tag. In general, for XML, the next node is just pushed into the tree. * * @param parser the current XML parser * @param node the current node * @param next the node that needs an action * * @return the action code for the next node */ int openAction(XmlParser parser, QName node, QName next) throws XmlParseException { String nodeName = node.getName(); /* if (nodeName.equals("#document")) { QDocument document = (QDocument) node; switch (next.getNodeType()) { case Node.TEXT_NODE: if (isWhitespaceOnly(next.getNodeValue())) return PUSH; // XXX: ignore break; case Node.COMMENT_NODE: case Node.PROCESSING_INSTRUCTION_NODE: return PUSH; } if (document.getDocumentElement() == null && next.getNodeType() == Node.ELEMENT_NODE) { document.setDocumentElement((Element) next); return PUSH; } Element elt = document.getDocumentElement(); return PUSH; } else return PUSH; */ return PUSH; } /** * Returns the action to be performed with the next node on a close * tag. In general, for XML, the current node is changed to its parent * * @param parser the current XML parser * @param node the current node * @param tagEnd the name of the close tag * * @return the action code for the next node */ int elementCloseAction(XmlParser parser, QName node, String tagEnd) throws XmlParseException { String nodeName = node.getName(); if (nodeName.equals("#document") && tagEnd.equals("")) return POP; else if (nodeName.equals(tagEnd)) return POP; else { String expect = nodeName; if (expect.equals("#document")) expect = L.l("end of document"); else expect = "`</" + expect + ">'"; if (tagEnd.equals("")) tagEnd = L.l("end of file"); else tagEnd = "`</" + tagEnd + ">'"; throw parser.error(L.l("expected {0} at {1}", expect, tagEnd)); } } }