| /* |
| * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. |
| */ |
| |
| /* |
| * Copyright 2005 The Apache Software Foundation. |
| * |
| * 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.sun.xml.internal.stream.dtd; |
| import com.sun.xml.internal.stream.dtd.nonvalidating.DTDGrammar; |
| import com.sun.xml.internal.stream.dtd.nonvalidating.XMLAttributeDecl; |
| import com.sun.xml.internal.stream.dtd.nonvalidating.XMLElementDecl; |
| import com.sun.xml.internal.stream.dtd.nonvalidating.XMLSimpleType; |
| import com.sun.org.apache.xerces.internal.impl.Constants; |
| import com.sun.org.apache.xerces.internal.impl.XMLEntityManager; |
| import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; |
| import com.sun.org.apache.xerces.internal.util.SymbolTable; |
| import com.sun.org.apache.xerces.internal.util.XMLChar; |
| import com.sun.org.apache.xerces.internal.util.XMLSymbols; |
| import com.sun.org.apache.xerces.internal.xni.Augmentations; |
| import com.sun.org.apache.xerces.internal.xni.QName; |
| import com.sun.org.apache.xerces.internal.xni.NamespaceContext; |
| import com.sun.org.apache.xerces.internal.xni.XMLAttributes; |
| import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; |
| import com.sun.org.apache.xerces.internal.xni.XMLLocator; |
| import com.sun.org.apache.xerces.internal.xni.XMLString; |
| import com.sun.org.apache.xerces.internal.xni.XNIException; |
| import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; |
| import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; |
| import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource; |
| import javax.xml.XMLConstants; |
| |
| /* |
| * @author Eric Ye, IBM |
| * @author Andy Clark, IBM |
| * @author Jeffrey Rodriguez IBM |
| * @author Neil Graham, IBM |
| * @author Sunitha Reddy, Sun Microsystems |
| */ |
| |
| public class DTDGrammarUtil { |
| |
| |
| /** Property identifier: symbol table. */ |
| protected static final String SYMBOL_TABLE = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; |
| |
| protected static final String NAMESPACES = |
| Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE; |
| |
| |
| /** Compile to true to debug attributes. */ |
| private static final boolean DEBUG_ATTRIBUTES = false; |
| |
| /** Compile to true to debug element children. */ |
| private static final boolean DEBUG_ELEMENT_CHILDREN = false; |
| |
| protected DTDGrammar fDTDGrammar = null; |
| /** Namespaces. */ |
| protected boolean fNamespaces; |
| |
| /** Symbol table. */ |
| protected SymbolTable fSymbolTable = null; |
| |
| /** Current element index. */ |
| private int fCurrentElementIndex = -1; |
| |
| /** Current content spec type. */ |
| private int fCurrentContentSpecType = -1; |
| |
| /** Content spec type stack. */ |
| private boolean[] fElementContentState = new boolean[8]; |
| |
| /** Element depth. */ |
| private int fElementDepth = -1; |
| |
| /** True if inside of element content. */ |
| private boolean fInElementContent = false; |
| |
| /** Temporary atribute declaration. */ |
| private XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl(); |
| |
| /** Temporary qualified name. */ |
| private QName fTempQName = new QName(); |
| |
| /** Temporary string buffers. */ |
| private StringBuffer fBuffer = new StringBuffer(); |
| |
| private NamespaceContext fNamespaceContext = null; |
| |
| /** Default constructor. */ |
| public DTDGrammarUtil(SymbolTable symbolTable) { |
| fSymbolTable = symbolTable; |
| } |
| |
| public DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable) { |
| fDTDGrammar = grammar; |
| fSymbolTable = symbolTable; |
| } |
| |
| public DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable, |
| NamespaceContext namespaceContext) { |
| fDTDGrammar = grammar; |
| fSymbolTable = symbolTable; |
| fNamespaceContext = namespaceContext; |
| } |
| |
| /* |
| * Resets the component. The component can query the component manager |
| * about any features and properties that affect the operation of the |
| * component. |
| * |
| * @param componentManager The component manager. |
| * |
| * @throws SAXException Thrown by component on finitialization error. |
| * For example, if a feature or property is |
| * required for the operation of the component, the |
| * component manager may throw a |
| * SAXNotRecognizedException or a |
| * SAXNotSupportedException. |
| */ |
| public void reset(XMLComponentManager componentManager) |
| throws XMLConfigurationException { |
| |
| fDTDGrammar = null; |
| fInElementContent = false; |
| fCurrentElementIndex = -1; |
| fCurrentContentSpecType = -1; |
| fNamespaces = componentManager.getFeature(NAMESPACES, true); |
| fSymbolTable = (SymbolTable) componentManager.getProperty( |
| Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY); |
| fElementDepth = -1; |
| } |
| |
| |
| /** |
| * The start of an element. |
| * |
| * @param element The name of the element. |
| * @param attributes The element attributes. |
| * @param augs Additional information that may include infoset augmentations |
| * |
| * @throws XNIException Thrown by handler to signal an error. |
| */ |
| public void startElement(QName element, XMLAttributes attributes) throws XNIException { |
| handleStartElement(element, attributes); |
| } |
| |
| /** |
| * The end of an element. |
| * |
| * @param element The name of the element. |
| * @param augs Additional information that may include infoset augmentations |
| * |
| * @throws XNIException Thrown by handler to signal an error. |
| */ |
| public void endElement(QName element) throws XNIException { |
| handleEndElement(element); |
| } |
| |
| /** |
| * The start of a CDATA section. |
| * @param augs Additional information that may include infoset augmentations |
| * |
| * @throws XNIException Thrown by handler to signal an error. |
| */ |
| public void startCDATA(Augmentations augs) throws XNIException { |
| } |
| |
| /** |
| * The end of a CDATA section. |
| * @param augs Additional information that may include infoset augmentations |
| * |
| * @throws XNIException Thrown by handler to signal an error. |
| */ |
| public void endCDATA(Augmentations augs) throws XNIException { |
| } |
| |
| |
| |
| /** Add default attributes and validate. */ |
| public void addDTDDefaultAttrs(QName elementName, XMLAttributes attributes) |
| throws XNIException { |
| |
| int elementIndex; |
| elementIndex = fDTDGrammar.getElementDeclIndex(elementName); |
| // is there anything to do? |
| if (elementIndex == -1 || fDTDGrammar == null) { |
| return; |
| } |
| |
| // |
| // Check after all specified attrs are scanned |
| // (1) report error for REQUIRED attrs that are missing (V_TAGc) |
| // (2) add default attrs (FIXED and NOT_FIXED) |
| // |
| int attlistIndex = fDTDGrammar.getFirstAttributeDeclIndex(elementIndex); |
| |
| while (attlistIndex != -1) { |
| |
| fDTDGrammar.getAttributeDecl(attlistIndex, fTempAttDecl); |
| |
| if (DEBUG_ATTRIBUTES) { |
| if (fTempAttDecl != null) { |
| XMLElementDecl elementDecl = new XMLElementDecl(); |
| fDTDGrammar.getElementDecl(elementIndex, elementDecl); |
| System.out.println("element: " + (elementDecl.name.localpart)); |
| System.out.println("attlistIndex " + attlistIndex + "\n" + |
| "attName : '" + (fTempAttDecl.name.localpart) + "'\n" |
| + "attType : " + fTempAttDecl.simpleType.type + "\n" |
| + "attDefaultType : " + fTempAttDecl.simpleType.defaultType + "\n" |
| + "attDefaultValue : '" + fTempAttDecl.simpleType.defaultValue + "'\n" |
| + attributes.getLength() + "\n" |
| ); |
| } |
| } |
| String attPrefix = fTempAttDecl.name.prefix; |
| String attLocalpart = fTempAttDecl.name.localpart; |
| String attRawName = fTempAttDecl.name.rawname; |
| String attType = getAttributeTypeName(fTempAttDecl); |
| int attDefaultType = fTempAttDecl.simpleType.defaultType; |
| String attValue = null; |
| |
| if (fTempAttDecl.simpleType.defaultValue != null) { |
| attValue = fTempAttDecl.simpleType.defaultValue; |
| } |
| boolean specified = false; |
| boolean required = attDefaultType == XMLSimpleType.DEFAULT_TYPE_REQUIRED; |
| boolean cdata = attType == XMLSymbols.fCDATASymbol; |
| |
| if (!cdata || required || attValue != null) { |
| |
| //check whether attribute is a namespace declaration |
| if (fNamespaceContext != null && attRawName.startsWith(XMLConstants.XMLNS_ATTRIBUTE)) { |
| String prefix = ""; |
| int pos = attRawName.indexOf(':'); |
| if (pos != -1) { |
| prefix = attRawName.substring(0, pos); |
| } else { |
| prefix = attRawName; |
| } |
| prefix = fSymbolTable.addSymbol(prefix); |
| if (!((com.sun.org.apache.xerces.internal.util. |
| NamespaceSupport) fNamespaceContext). |
| containsPrefixInCurrentContext(prefix)) { |
| fNamespaceContext.declarePrefix(prefix, attValue); |
| } |
| specified = true; |
| } else { |
| |
| int attrCount = attributes.getLength(); |
| for (int i = 0; i < attrCount; i++) { |
| if (attributes.getQName(i) == attRawName) { |
| specified = true; |
| break; |
| } |
| } |
| |
| } |
| |
| } |
| |
| if (!specified) { |
| if (attValue != null) { |
| if (fNamespaces) { |
| int index = attRawName.indexOf(':'); |
| if (index != -1) { |
| attPrefix = attRawName.substring(0, index); |
| attPrefix = fSymbolTable.addSymbol(attPrefix); |
| attLocalpart = attRawName.substring(index + 1); |
| attLocalpart = fSymbolTable.addSymbol(attLocalpart); |
| } |
| } |
| fTempQName.setValues(attPrefix, attLocalpart, attRawName, |
| fTempAttDecl.name.uri); |
| int newAttr = attributes.addAttribute(fTempQName, attType, |
| attValue); |
| } |
| } |
| attlistIndex = fDTDGrammar.getNextAttributeDeclIndex(attlistIndex); |
| } |
| |
| // now iterate through the expanded attributes for |
| // 1. if every attribute seen is declared in the DTD |
| // 2. check if the VC: default_fixed holds |
| // 3. validate every attribute. |
| int attrCount = attributes.getLength(); |
| for (int i = 0; i < attrCount; i++) { |
| String attrRawName = attributes.getQName(i); |
| boolean declared = false; |
| int position = |
| fDTDGrammar.getFirstAttributeDeclIndex(elementIndex); |
| while (position != -1) { |
| fDTDGrammar.getAttributeDecl(position, fTempAttDecl); |
| if (fTempAttDecl.name.rawname == attrRawName) { |
| // found the match att decl, |
| declared = true; |
| break; |
| } |
| position = fDTDGrammar.getNextAttributeDeclIndex(position); |
| } |
| if (!declared) { |
| continue; |
| } |
| |
| String type = getAttributeTypeName(fTempAttDecl); |
| attributes.setType(i, type); |
| |
| boolean changedByNormalization = false; |
| if (attributes.isSpecified(i) && type != XMLSymbols.fCDATASymbol) { |
| changedByNormalization = normalizeAttrValue(attributes, i); |
| } |
| } // for all attributes |
| |
| } // addDTDDefaultAttrsAndValidate(int,XMLAttrList) |
| |
| |
| /** |
| * Normalize the attribute value of a non CDATA attributes collapsing |
| * sequences of space characters (x20) |
| * |
| * @param attributes The list of attributes |
| * @param index The index of the attribute to normalize |
| */ |
| private boolean normalizeAttrValue(XMLAttributes attributes, int index) { |
| // vars |
| boolean leadingSpace = true; |
| boolean spaceStart = false; |
| boolean readingNonSpace = false; |
| int count = 0; |
| int eaten = 0; |
| String attrValue = attributes.getValue(index); |
| char[] attValue = new char[attrValue.length()]; |
| |
| fBuffer.setLength(0); |
| attrValue.getChars(0, attrValue.length(), attValue, 0); |
| for (int i = 0; i < attValue.length; i++) { |
| |
| if (attValue[i] == ' ') { |
| |
| // now the tricky part |
| if (readingNonSpace) { |
| spaceStart = true; |
| readingNonSpace = false; |
| } |
| |
| if (spaceStart && !leadingSpace) { |
| spaceStart = false; |
| fBuffer.append(attValue[i]); |
| count++; |
| } else { |
| if (leadingSpace || !spaceStart) { |
| eaten++; |
| } |
| } |
| |
| } else { |
| readingNonSpace = true; |
| spaceStart = false; |
| leadingSpace = false; |
| fBuffer.append(attValue[i]); |
| count++; |
| } |
| } |
| |
| // check if the last appended character is a space. |
| if (count > 0 && fBuffer.charAt(count - 1) == ' ') { |
| fBuffer.setLength(count - 1); |
| |
| } |
| String newValue = fBuffer.toString(); |
| attributes.setValue(index, newValue); |
| return !attrValue.equals(newValue); |
| } |
| |
| |
| |
| /** convert attribute type from ints to strings */ |
| private String getAttributeTypeName(XMLAttributeDecl attrDecl) { |
| |
| switch (attrDecl.simpleType.type) { |
| case XMLSimpleType.TYPE_ENTITY: { |
| return attrDecl.simpleType.list ? XMLSymbols.fENTITIESSymbol : |
| XMLSymbols.fENTITYSymbol; |
| } |
| case XMLSimpleType.TYPE_ENUMERATION: { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append('('); |
| for (int i = 0; i < attrDecl.simpleType.enumeration.length; i++) { |
| if (i > 0) { |
| buffer.append("|"); |
| } |
| buffer.append(attrDecl.simpleType.enumeration[i]); |
| } |
| buffer.append(')'); |
| return fSymbolTable.addSymbol(buffer.toString()); |
| } |
| case XMLSimpleType.TYPE_ID: { |
| return XMLSymbols.fIDSymbol; |
| } |
| case XMLSimpleType.TYPE_IDREF: { |
| return attrDecl.simpleType.list ? XMLSymbols.fIDREFSSymbol : |
| XMLSymbols.fIDREFSymbol; |
| } |
| case XMLSimpleType.TYPE_NMTOKEN: { |
| return attrDecl.simpleType.list ? XMLSymbols.fNMTOKENSSymbol : |
| XMLSymbols.fNMTOKENSymbol; |
| } |
| case XMLSimpleType.TYPE_NOTATION: { |
| return XMLSymbols.fNOTATIONSymbol; |
| } |
| } |
| return XMLSymbols.fCDATASymbol; |
| |
| } |
| |
| |
| /** ensure element stack capacity */ |
| private void ensureStackCapacity(int newElementDepth) { |
| if (newElementDepth == fElementContentState.length) { |
| boolean[] newStack = new boolean[newElementDepth * 2]; |
| System.arraycopy(this.fElementContentState, 0, newStack, 0, |
| newElementDepth); |
| fElementContentState = newStack; |
| } |
| } |
| |
| |
| |
| /** Handle element |
| * @return true if validator is removed from the pipeline |
| */ |
| protected void handleStartElement(QName element, XMLAttributes attributes) throws XNIException { |
| |
| if (fDTDGrammar == null) { |
| fCurrentElementIndex = -1; |
| fCurrentContentSpecType = -1; |
| fInElementContent = false; |
| return; |
| } else { |
| fCurrentElementIndex = fDTDGrammar.getElementDeclIndex(element); |
| fCurrentContentSpecType = fDTDGrammar.getContentSpecType( |
| fCurrentElementIndex); |
| //handleDTDDefaultAttrs(element,attributes); |
| addDTDDefaultAttrs(element, attributes); |
| } |
| |
| fInElementContent = fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN; |
| fElementDepth++; |
| ensureStackCapacity(fElementDepth); |
| fElementContentState[fElementDepth] = fInElementContent; |
| } |
| |
| |
| /** Handle end element. */ |
| protected void handleEndElement(QName element) throws XNIException { |
| if (fDTDGrammar == null) return; |
| fElementDepth--; |
| if (fElementDepth < -1) { |
| throw new RuntimeException("FWK008 Element stack underflow"); |
| } |
| if (fElementDepth < 0) { |
| fCurrentElementIndex = -1; |
| fCurrentContentSpecType = -1; |
| fInElementContent = false; |
| return; |
| } |
| fInElementContent = fElementContentState[fElementDepth]; |
| } |
| |
| public boolean isInElementContent() { |
| return fInElementContent; |
| } |
| |
| public boolean isIgnorableWhiteSpace(XMLString text) { |
| if (isInElementContent()) { |
| for (int i = text.offset; i < text.offset + text.length; i++) { |
| if (!XMLChar.isSpace(text.ch[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| } |