/* * 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.nonvalidating; import java.util.Hashtable; import java.util.ArrayList; import java.util.List; import com.sun.org.apache.xerces.internal.util.SymbolTable; 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.util.XMLSymbols; import com.sun.org.apache.xerces.internal.xni.XMLLocator; import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; 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.XMLDTDContentModelSource; import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDSource; /** * A DTD grammar. This class implements the XNI handler interfaces * for DTD information so that it can build the approprate validation * structures automatically from the callbacks. * * @author Eric Ye, IBM * @author Jeffrey Rodriguez, IBM * @author Andy Clark, IBM * @author Neil Graham, IBM * * @version $Id: DTDGrammar.java,v 1.1 2005/12/02 07:54:08 neerajbj Exp $ */ public class DTDGrammar { /** Top level scope (-1). */ public static final int TOP_LEVEL_SCOPE = -1; // private /** Chunk shift (8). */ private static final int CHUNK_SHIFT = 8; // 2^8 = 256 /** Chunk size (1 << CHUNK_SHIFT). */ private static final int CHUNK_SIZE = (1 << CHUNK_SHIFT); /** Chunk mask (CHUNK_SIZE - 1). */ private static final int CHUNK_MASK = CHUNK_SIZE - 1; /** Initial chunk count (1 << (10 - CHUNK_SHIFT)). */ private static final int INITIAL_CHUNK_COUNT = (1 << (10 - CHUNK_SHIFT)); // 2^10 = 1k /** List flag (0x80). */ private static final short LIST_FLAG = 0x80; /** List mask (~LIST_FLAG). */ private static final short LIST_MASK = ~LIST_FLAG; // debugging /** Debug DTDGrammar. */ private static final boolean DEBUG = false; // // Data // protected XMLDTDSource fDTDSource = null; protected XMLDTDContentModelSource fDTDContentModelSource = null; /** Current element index. */ protected int fCurrentElementIndex; /** Current attribute index. */ protected int fCurrentAttributeIndex; /** fReadingExternalDTD */ protected boolean fReadingExternalDTD = false; /** Symbol table. */ private SymbolTable fSymbolTable; private ArrayList notationDecls = new ArrayList(); // element declarations /** Number of element declarations. */ private int fElementDeclCount = 0; /** Element declaration name. */ private QName fElementDeclName[][] = new QName[INITIAL_CHUNK_COUNT][]; /** * Element declaration type. * @see XMLElementDecl */ private short fElementDeclType[][] = new short[INITIAL_CHUNK_COUNT][]; /** First attribute declaration of an element declaration. */ private int fElementDeclFirstAttributeDeclIndex[][] = new int[INITIAL_CHUNK_COUNT][]; /** Last attribute declaration of an element declaration. */ private int fElementDeclLastAttributeDeclIndex[][] = new int[INITIAL_CHUNK_COUNT][]; // attribute declarations /** Number of attribute declarations. */ private int fAttributeDeclCount = 0 ; /** Attribute declaration name. */ private QName fAttributeDeclName[][] = new QName[INITIAL_CHUNK_COUNT][]; /** * Attribute declaration type. * @see XMLAttributeDecl */ private short fAttributeDeclType[][] = new short[INITIAL_CHUNK_COUNT][]; /** Attribute declaration enumeration values. */ private String[] fAttributeDeclEnumeration[][] = new String[INITIAL_CHUNK_COUNT][][]; private short fAttributeDeclDefaultType[][] = new short[INITIAL_CHUNK_COUNT][]; private String fAttributeDeclDefaultValue[][] = new String[INITIAL_CHUNK_COUNT][]; private String fAttributeDeclNonNormalizedDefaultValue[][] = new String[INITIAL_CHUNK_COUNT][]; private int fAttributeDeclNextAttributeDeclIndex[][] = new int[INITIAL_CHUNK_COUNT][]; /** Element index mapping table. */ private QNameHashtable fElementIndexMap = new QNameHashtable(); /** Temporary qualified name. */ private QName fQName = new QName(); /** Temporary Attribute decl. */ protected XMLAttributeDecl fAttributeDecl = new XMLAttributeDecl(); /** Element declaration. */ private XMLElementDecl fElementDecl = new XMLElementDecl(); /** Simple type. */ private XMLSimpleType fSimpleType = new XMLSimpleType(); /** table of XMLElementDecl */ Hashtable fElementDeclTab = new Hashtable(); /** Default constructor. */ public DTDGrammar(SymbolTable symbolTable) { fSymbolTable = symbolTable; } public int getAttributeDeclIndex(int elementDeclIndex, String attributeDeclName) { if (elementDeclIndex == -1) { return -1; } int attDefIndex = getFirstAttributeDeclIndex(elementDeclIndex); while (attDefIndex != -1) { getAttributeDecl(attDefIndex, fAttributeDecl); if (fAttributeDecl.name.rawname == attributeDeclName || attributeDeclName.equals(fAttributeDecl.name.rawname) ) { return attDefIndex; } attDefIndex = getNextAttributeDeclIndex(attDefIndex); } return -1; } /** * The start of the DTD. * * @param locator The document locator, or null if the document * location cannot be reported during the parsing of * the document DTD. However, it is <em>strongly</em> * recommended that a locator be supplied that can * at least report the base system identifier of the * DTD. * * @param augs Additional information that may include infoset * augmentations. * @throws XNIException Thrown by handler to signal an error. */ public void startDTD(XMLLocator locator, Augmentations augs) throws XNIException { } // startDTD(XMLLocator) // startExternalSubset(Augmentations) // endExternalSubset(Augmentations) /** * An element declaration. * * @param name The name of the element. * @param contentModel The element content model. * @param augs Additional information that may include infoset * augmentations. * @throws XNIException Thrown by handler to signal an error. */ public void elementDecl(String name, String contentModel, Augmentations augs) throws XNIException { XMLElementDecl tmpElementDecl = (XMLElementDecl) fElementDeclTab.get(name) ; if ( tmpElementDecl != null ) { if (tmpElementDecl.type == -1) { fCurrentElementIndex = getElementDeclIndex(name); } else { // duplicate element, ignored. return; } } else { fCurrentElementIndex = createElementDecl();//create element decl } XMLElementDecl elementDecl = new XMLElementDecl(); QName elementName = new QName(null, name, name, null); elementDecl.name.setValues(elementName); elementDecl.scope= -1; if (contentModel.equals("EMPTY")) { elementDecl.type = XMLElementDecl.TYPE_EMPTY; } else if (contentModel.equals("ANY")) { elementDecl.type = XMLElementDecl.TYPE_ANY; } else if (contentModel.startsWith("(") ) { if (contentModel.indexOf("#PCDATA") > 0 ) { elementDecl.type = XMLElementDecl.TYPE_MIXED; } else { elementDecl.type = XMLElementDecl.TYPE_CHILDREN; } } //add(or set) this elementDecl to the local cache this.fElementDeclTab.put(name, elementDecl ); fElementDecl = elementDecl; if ( DEBUG ) { System.out.println( "name = " + fElementDecl.name.localpart ); System.out.println( "Type = " + fElementDecl.type ); } setElementDecl(fCurrentElementIndex, fElementDecl );//set internal structure int chunk = fCurrentElementIndex >> CHUNK_SHIFT; ensureElementDeclCapacity(chunk); } /** * An attribute declaration. * * @param elementName The name of the element that this attribute * is associated with. * @param attributeName The name of the attribute. * @param type The attribute type. This value will be one of * the following: "CDATA", "ENTITY", "ENTITIES", * "ENUMERATION", "ID", "IDREF", "IDREFS", * "NMTOKEN", "NMTOKENS", or "NOTATION". * @param enumeration If the type has the value "ENUMERATION", this * array holds the allowed attribute values; * otherwise, this array is null. * @param defaultType The attribute default type. This value will be * one of the following: "#FIXED", "#IMPLIED", * "#REQUIRED", or null. * @param defaultValue The attribute default value, or null if no * default value is specified. * @param nonNormalizedDefaultValue The attribute default value with no normalization * performed, or null if no default value is specified. * * @param augs Additional information that may include infoset * augmentations. * @throws XNIException Thrown by handler to signal an error. */ public void attributeDecl(String elementName, String attributeName, String type, String[] enumeration, String defaultType, XMLString defaultValue, XMLString nonNormalizedDefaultValue, Augmentations augs) throws XNIException { if (type != XMLSymbols.fCDATASymbol && defaultValue != null) { normalizeDefaultAttrValue(defaultValue); } if ( this.fElementDeclTab.containsKey( (String) elementName) ) { //if ElementDecl has already being created in the Grammar then remove from table, //this.fElementDeclTab.remove( (String) elementName ); } // then it is forward reference to a element decl, create the elementDecl first. else { fCurrentElementIndex = createElementDecl();//create element decl XMLElementDecl elementDecl = new XMLElementDecl(); elementDecl.name.setValues(null, elementName, elementName, null); elementDecl.scope= -1; //add(or set) this elementDecl to the local cache this.fElementDeclTab.put(elementName, elementDecl ); //set internal structure setElementDecl(fCurrentElementIndex, elementDecl ); } //Get Grammar index to grammar array int elementIndex = getElementDeclIndex(elementName); //return, when more than one definition is provided for the same attribute of given element type //only the first declaration is binding and later declarations are ignored if (getAttributeDeclIndex(elementIndex, attributeName) != -1) { return; } fCurrentAttributeIndex = createAttributeDecl();// Create current Attribute Decl fSimpleType.clear(); if ( defaultType != null ) { if ( defaultType.equals( "#FIXED") ) { fSimpleType.defaultType = fSimpleType.DEFAULT_TYPE_FIXED; } else if ( defaultType.equals( "#IMPLIED") ) { fSimpleType.defaultType = fSimpleType.DEFAULT_TYPE_IMPLIED; } else if ( defaultType.equals( "#REQUIRED") ) { fSimpleType.defaultType = fSimpleType.DEFAULT_TYPE_REQUIRED; } } if ( DEBUG ) { System.out.println("defaultvalue = " + defaultValue.toString() ); } fSimpleType.defaultValue = defaultValue!=null ? defaultValue.toString() : null; fSimpleType.nonNormalizedDefaultValue = nonNormalizedDefaultValue!=null ? nonNormalizedDefaultValue.toString() : null; fSimpleType.enumeration = enumeration; if (type.equals("CDATA")) { fSimpleType.type = XMLSimpleType.TYPE_CDATA; } else if ( type.equals("ID") ) { fSimpleType.type = XMLSimpleType.TYPE_ID; } else if ( type.startsWith("IDREF") ) { fSimpleType.type = XMLSimpleType.TYPE_IDREF; if (type.indexOf("S") > 0) { fSimpleType.list = true; } } else if (type.equals("ENTITIES")) { fSimpleType.type = XMLSimpleType.TYPE_ENTITY; fSimpleType.list = true; } else if (type.equals("ENTITY")) { fSimpleType.type = XMLSimpleType.TYPE_ENTITY; } else if (type.equals("NMTOKENS")) { fSimpleType.type = XMLSimpleType.TYPE_NMTOKEN; fSimpleType.list = true; } else if (type.equals("NMTOKEN")) { fSimpleType.type = XMLSimpleType.TYPE_NMTOKEN; } else if (type.startsWith("NOTATION") ) { fSimpleType.type = XMLSimpleType.TYPE_NOTATION; } else if (type.startsWith("ENUMERATION") ) { fSimpleType.type = XMLSimpleType.TYPE_ENUMERATION; } else { // REVISIT: Report error message. -Ac System.err.println("!!! unknown attribute type "+type); } // REVISIT: The datatype should be stored with the attribute value // and not special-cased in the XMLValidator. -Ac //fSimpleType.datatypeValidator = fDatatypeValidatorFactory.createDatatypeValidator(type, null, facets, fSimpleType.list); fQName.setValues(null, attributeName, attributeName, null); fAttributeDecl.setValues( fQName, fSimpleType, false ); setAttributeDecl(elementIndex, fCurrentAttributeIndex, fAttributeDecl); int chunk = fCurrentAttributeIndex >> CHUNK_SHIFT; ensureAttributeDeclCapacity(chunk); } // attributeDecl(String,String,String,String[],String,XMLString,XMLString, Augmentations) /** Returns the symbol table. */ public SymbolTable getSymbolTable() { return fSymbolTable; } // getSymbolTable():SymbolTable /** * Returns the index of the first element declaration. This index * is then used to query more information about the element declaration. * * @see #getNextElementDeclIndex * @see #getElementDecl */ public int getFirstElementDeclIndex() { return fElementDeclCount >= 0 ? 0 : -1; } // getFirstElementDeclIndex():int /** * Returns the next index of the element declaration following the * specified element declaration. * * @param elementDeclIndex The element declaration index. */ public int getNextElementDeclIndex(int elementDeclIndex) { return elementDeclIndex < fElementDeclCount - 1 ? elementDeclIndex + 1 : -1; } // getNextElementDeclIndex(int):int /** * getElementDeclIndex * * @param elementDeclName * * @return index of the elementDeclName in scope */ public int getElementDeclIndex(String elementDeclName) { int mapping = fElementIndexMap.get(elementDeclName); //System.out.println("getElementDeclIndex("+elementDeclName+") -> "+mapping); return mapping; } // getElementDeclIndex(String):int /** Returns the element decl index. * @param elementDeclQName qualilfied name of the element */ public int getElementDeclIndex(QName elementDeclQName) { return getElementDeclIndex(elementDeclQName.rawname); } // getElementDeclIndex(QName):int /** make separate function for getting contentSpecType of element. * we can avoid setting of the element values. */ public short getContentSpecType(int elementIndex){ if (elementIndex < 0 || elementIndex >= fElementDeclCount) { return -1 ; } int chunk = elementIndex >> CHUNK_SHIFT; int index = elementIndex & CHUNK_MASK; if(fElementDeclType[chunk][index] == -1){ return -1 ; } else{ return (short) (fElementDeclType[chunk][index] & LIST_MASK); } } /** * getElementDecl * * @param elementDeclIndex * @param elementDecl The values of this structure are set by this call. * * @return True if find the element, False otherwise. */ public boolean getElementDecl(int elementDeclIndex, XMLElementDecl elementDecl) { if (elementDeclIndex < 0 || elementDeclIndex >= fElementDeclCount) { return false; } int chunk = elementDeclIndex >> CHUNK_SHIFT; int index = elementDeclIndex & CHUNK_MASK; elementDecl.name.setValues(fElementDeclName[chunk][index]); if (fElementDeclType[chunk][index] == -1) { elementDecl.type = -1; elementDecl.simpleType.list = false; } else { elementDecl.type = (short) (fElementDeclType[chunk][index] & LIST_MASK); elementDecl.simpleType.list = (fElementDeclType[chunk][index] & LIST_FLAG) != 0; } elementDecl.simpleType.defaultType = -1; elementDecl.simpleType.defaultValue = null; return true; } // REVISIT: Make this getAttributeDeclCount/getAttributeDeclAt. -Ac /** * getFirstAttributeDeclIndex * * @param elementDeclIndex * * @return index of the first attribute for element declaration elementDeclIndex */ public int getFirstAttributeDeclIndex(int elementDeclIndex) { int chunk = elementDeclIndex >> CHUNK_SHIFT; int index = elementDeclIndex & CHUNK_MASK; return fElementDeclFirstAttributeDeclIndex[chunk][index]; } // getFirstAttributeDeclIndex /** * getNextAttributeDeclIndex * * @param attributeDeclIndex * * @return index of the next attribute of the attribute at attributeDeclIndex */ public int getNextAttributeDeclIndex(int attributeDeclIndex) { int chunk = attributeDeclIndex >> CHUNK_SHIFT; int index = attributeDeclIndex & CHUNK_MASK; return fAttributeDeclNextAttributeDeclIndex[chunk][index]; } /** * getAttributeDecl * * @param attributeDeclIndex * @param attributeDecl The values of this structure are set by this call. * * @return true if getAttributeDecl was able to fill in the value of attributeDecl */ public boolean getAttributeDecl(int attributeDeclIndex, XMLAttributeDecl attributeDecl) { if (attributeDeclIndex < 0 || attributeDeclIndex >= fAttributeDeclCount) { return false; } int chunk = attributeDeclIndex >> CHUNK_SHIFT; int index = attributeDeclIndex & CHUNK_MASK; attributeDecl.name.setValues(fAttributeDeclName[chunk][index]); short attributeType; boolean isList; if (fAttributeDeclType[chunk][index] == -1) { attributeType = -1; isList = false; } else { attributeType = (short) (fAttributeDeclType[chunk][index] & LIST_MASK); isList = (fAttributeDeclType[chunk][index] & LIST_FLAG) != 0; } attributeDecl.simpleType.setValues(attributeType,fAttributeDeclName[chunk][index].localpart, fAttributeDeclEnumeration[chunk][index], isList, fAttributeDeclDefaultType[chunk][index], fAttributeDeclDefaultValue[chunk][index], fAttributeDeclNonNormalizedDefaultValue[chunk][index]); return true; } // getAttributeDecl /** * Returns whether the given attribute is of type CDATA or not * * @param elName The element name. * @param atName The attribute name. * * @return true if the attribute is of type CDATA */ public boolean isCDATAAttribute(QName elName, QName atName) { int elDeclIdx = getElementDeclIndex(elName); if (getAttributeDecl(elDeclIdx, fAttributeDecl) && fAttributeDecl.simpleType.type != XMLSimpleType.TYPE_CDATA){ return false; } return true; } public void printElements( ) { int elementDeclIndex = 0; XMLElementDecl elementDecl = new XMLElementDecl(); while (getElementDecl(elementDeclIndex++, elementDecl)) { System.out.println("element decl: "+elementDecl.name+ ", "+ elementDecl.name.rawname ); } } public void printAttributes(int elementDeclIndex) { int attributeDeclIndex = getFirstAttributeDeclIndex(elementDeclIndex); System.out.print(elementDeclIndex); System.out.print(" ["); while (attributeDeclIndex != -1) { System.out.print(' '); System.out.print(attributeDeclIndex); printAttribute(attributeDeclIndex); attributeDeclIndex = getNextAttributeDeclIndex(attributeDeclIndex); if (attributeDeclIndex != -1) { System.out.print(","); } } System.out.println(" ]"); } protected int createElementDecl() { int chunk = fElementDeclCount >> CHUNK_SHIFT; int index = fElementDeclCount & CHUNK_MASK; ensureElementDeclCapacity(chunk); fElementDeclName[chunk][index] = new QName(); fElementDeclType[chunk][index] = -1; fElementDeclFirstAttributeDeclIndex[chunk][index] = -1; fElementDeclLastAttributeDeclIndex[chunk][index] = -1; return fElementDeclCount++; } protected void setElementDecl(int elementDeclIndex, XMLElementDecl elementDecl) { if (elementDeclIndex < 0 || elementDeclIndex >= fElementDeclCount) { return; } int chunk = elementDeclIndex >> CHUNK_SHIFT; int index = elementDeclIndex & CHUNK_MASK; int scope = elementDecl.scope; fElementDeclName[chunk][index].setValues(elementDecl.name); fElementDeclType[chunk][index] = elementDecl.type; if (elementDecl.simpleType.list == true ) { fElementDeclType[chunk][index] |= LIST_FLAG; } fElementIndexMap.put(elementDecl.name.rawname, elementDeclIndex); } protected void setFirstAttributeDeclIndex(int elementDeclIndex, int newFirstAttrIndex){ if (elementDeclIndex < 0 || elementDeclIndex >= fElementDeclCount) { return; } int chunk = elementDeclIndex >> CHUNK_SHIFT; int index = elementDeclIndex & CHUNK_MASK; fElementDeclFirstAttributeDeclIndex[chunk][index] = newFirstAttrIndex; } protected int createAttributeDecl() { int chunk = fAttributeDeclCount >> CHUNK_SHIFT; int index = fAttributeDeclCount & CHUNK_MASK; ensureAttributeDeclCapacity(chunk); fAttributeDeclName[chunk][index] = new QName(); fAttributeDeclType[chunk][index] = -1; fAttributeDeclEnumeration[chunk][index] = null; fAttributeDeclDefaultType[chunk][index] = XMLSimpleType.DEFAULT_TYPE_IMPLIED; fAttributeDeclDefaultValue[chunk][index] = null; fAttributeDeclNonNormalizedDefaultValue[chunk][index] = null; fAttributeDeclNextAttributeDeclIndex[chunk][index] = -1; return fAttributeDeclCount++; } protected void setAttributeDecl(int elementDeclIndex, int attributeDeclIndex, XMLAttributeDecl attributeDecl) { int attrChunk = attributeDeclIndex >> CHUNK_SHIFT; int attrIndex = attributeDeclIndex & CHUNK_MASK; fAttributeDeclName[attrChunk][attrIndex].setValues(attributeDecl.name); fAttributeDeclType[attrChunk][attrIndex] = attributeDecl.simpleType.type; if (attributeDecl.simpleType.list) { fAttributeDeclType[attrChunk][attrIndex] |= LIST_FLAG; } fAttributeDeclEnumeration[attrChunk][attrIndex] = attributeDecl.simpleType.enumeration; fAttributeDeclDefaultType[attrChunk][attrIndex] = attributeDecl.simpleType.defaultType; fAttributeDeclDefaultValue[attrChunk][attrIndex] = attributeDecl.simpleType.defaultValue; fAttributeDeclNonNormalizedDefaultValue[attrChunk][attrIndex] = attributeDecl.simpleType.nonNormalizedDefaultValue; int elemChunk = elementDeclIndex >> CHUNK_SHIFT; int elemIndex = elementDeclIndex & CHUNK_MASK; int index = fElementDeclFirstAttributeDeclIndex[elemChunk][elemIndex]; while (index != -1) { if (index == attributeDeclIndex) { break; } attrChunk = index >> CHUNK_SHIFT; attrIndex = index & CHUNK_MASK; index = fAttributeDeclNextAttributeDeclIndex[attrChunk][attrIndex]; } if (index == -1) { if (fElementDeclFirstAttributeDeclIndex[elemChunk][elemIndex] == -1) { fElementDeclFirstAttributeDeclIndex[elemChunk][elemIndex] = attributeDeclIndex; } else { index = fElementDeclLastAttributeDeclIndex[elemChunk][elemIndex]; attrChunk = index >> CHUNK_SHIFT; attrIndex = index & CHUNK_MASK; fAttributeDeclNextAttributeDeclIndex[attrChunk][attrIndex] = attributeDeclIndex; } fElementDeclLastAttributeDeclIndex[elemChunk][elemIndex] = attributeDeclIndex; } } public void notationDecl(String name, XMLResourceIdentifier identifier, Augmentations augs) throws XNIException { XMLNotationDecl notationDecl = new XMLNotationDecl(); notationDecl.setValues(name,identifier.getPublicId(),identifier.getLiteralSystemId(), identifier.getBaseSystemId()); notationDecls.add(notationDecl); } public List getNotationDecls(){ return notationDecls; } // // Private methods // private void printAttribute(int attributeDeclIndex) { XMLAttributeDecl attributeDecl = new XMLAttributeDecl(); if (getAttributeDecl(attributeDeclIndex, attributeDecl)) { System.out.print(" { "); System.out.print(attributeDecl.name.localpart); System.out.print(" }"); } } // printAttribute(int) private void ensureElementDeclCapacity(int chunk) { if (chunk >= fElementDeclName.length) { fElementDeclName = resize(fElementDeclName, fElementDeclName.length * 2); fElementDeclType = resize(fElementDeclType, fElementDeclType.length * 2); fElementDeclFirstAttributeDeclIndex = resize(fElementDeclFirstAttributeDeclIndex, fElementDeclFirstAttributeDeclIndex.length * 2); fElementDeclLastAttributeDeclIndex = resize(fElementDeclLastAttributeDeclIndex, fElementDeclLastAttributeDeclIndex.length * 2); } else if (fElementDeclName[chunk] != null) { return; } fElementDeclName[chunk] = new QName[CHUNK_SIZE]; fElementDeclType[chunk] = new short[CHUNK_SIZE]; fElementDeclFirstAttributeDeclIndex[chunk] = new int[CHUNK_SIZE]; fElementDeclLastAttributeDeclIndex[chunk] = new int[CHUNK_SIZE]; return; } private void ensureAttributeDeclCapacity(int chunk) { if (chunk >= fAttributeDeclName.length) { fAttributeDeclName = resize(fAttributeDeclName, fAttributeDeclName.length * 2); fAttributeDeclType = resize(fAttributeDeclType, fAttributeDeclType.length * 2); fAttributeDeclEnumeration = resize(fAttributeDeclEnumeration, fAttributeDeclEnumeration.length * 2); fAttributeDeclDefaultType = resize(fAttributeDeclDefaultType, fAttributeDeclDefaultType.length * 2); fAttributeDeclDefaultValue = resize(fAttributeDeclDefaultValue, fAttributeDeclDefaultValue.length * 2); fAttributeDeclNonNormalizedDefaultValue = resize(fAttributeDeclNonNormalizedDefaultValue, fAttributeDeclNonNormalizedDefaultValue.length * 2); fAttributeDeclNextAttributeDeclIndex = resize(fAttributeDeclNextAttributeDeclIndex, fAttributeDeclNextAttributeDeclIndex.length * 2); } else if (fAttributeDeclName[chunk] != null) { return; } fAttributeDeclName[chunk] = new QName[CHUNK_SIZE]; fAttributeDeclType[chunk] = new short[CHUNK_SIZE]; fAttributeDeclEnumeration[chunk] = new String[CHUNK_SIZE][]; fAttributeDeclDefaultType[chunk] = new short[CHUNK_SIZE]; fAttributeDeclDefaultValue[chunk] = new String[CHUNK_SIZE]; fAttributeDeclNonNormalizedDefaultValue[chunk] = new String[CHUNK_SIZE]; fAttributeDeclNextAttributeDeclIndex[chunk] = new int[CHUNK_SIZE]; return; } // resize chunks private static short[][] resize(short array[][], int newsize) { short newarray[][] = new short[newsize][]; System.arraycopy(array, 0, newarray, 0, array.length); return newarray; } private static int[][] resize(int array[][], int newsize) { int newarray[][] = new int[newsize][]; System.arraycopy(array, 0, newarray, 0, array.length); return newarray; } private static QName[][] resize(QName array[][], int newsize) { QName newarray[][] = new QName[newsize][]; System.arraycopy(array, 0, newarray, 0, array.length); return newarray; } private static String[][] resize(String array[][], int newsize) { String newarray[][] = new String[newsize][]; System.arraycopy(array, 0, newarray, 0, array.length); return newarray; } private static String[][][] resize(String array[][][], int newsize) { String newarray[][][] = new String[newsize] [][]; System.arraycopy(array, 0, newarray, 0, array.length); return newarray; } // // Classes // /** * A simple Hashtable implementation that takes a tuple (String, String) * as the key and a int as value. * * @author Eric Ye, IBM * @author Andy Clark, IBM */ protected static final class QNameHashtable { // // Constants // public static final boolean UNIQUE_STRINGS = true; /** Initial bucket size (4). */ private static final int INITIAL_BUCKET_SIZE = 4; // NOTE: Changed previous hashtable size from 512 to 101 so // that we get a better distribution for hashing. -Ac /** Hashtable size (101). */ private static final int HASHTABLE_SIZE = 101; // // Data // private Object[][] fHashTable = new Object[HASHTABLE_SIZE][]; // // Public methods // /** Associates the given value with the specified key tuple. */ public void put(String key, int value) { // REVISIT: Why +2? -Ac int hash = (hash(key)+2) % HASHTABLE_SIZE; Object[] bucket = fHashTable[hash]; if (bucket == null) { bucket = new Object[1 + 2*INITIAL_BUCKET_SIZE]; bucket[0] = new int[]{1}; bucket[1] = key; bucket[2] = new int[]{value}; fHashTable[hash] = bucket; } else { int count = ((int[])bucket[0])[0]; int offset = 1 + 2*count; if (offset == bucket.length) { int newSize = count + INITIAL_BUCKET_SIZE; Object[] newBucket = new Object[1 + 2*newSize]; System.arraycopy(bucket, 0, newBucket, 0, offset); bucket = newBucket; fHashTable[hash] = bucket; } boolean found = false; int j=1; for (int i=0; i<count; i++){ if ((String)bucket[j] == key) { ((int[])bucket[j+1])[0] = value; found = true; break; } j += 2; } if (! found) { bucket[offset++] = key; bucket[offset]= new int[]{value}; ((int[])bucket[0])[0] = ++count; } } //System.out.println("put("+key+" -> "+value+')'); //System.out.println("get("+key+") -> "+get(key)); } // put(int,String,String,int) /** Returns the value associated with the specified key tuple. */ public int get(String key) { int hash = (hash(key)+2) % HASHTABLE_SIZE; Object[] bucket = fHashTable[hash]; if (bucket == null) { return -1; } int count = ((int[])bucket[0])[0]; int j=1; for (int i=0; i<count; i++){ if ((String)bucket[j] == key) { return ((int[])bucket[j+1])[0]; } j += 2; } return -1; } // get(int,String,String) // // Protected methods // /** Returns a hash value for the specified symbol. */ protected int hash(String symbol) { if (symbol == null) { return 0; } int code = 0; int length = symbol.length(); for (int i = 0; i < length; i++) { code = code * 37 + symbol.charAt(i); } return code & 0x7FFFFFF; } // hash(String):int } // class QNameHashtable /** * Normalize the attribute value of a non CDATA default attribute * collapsing sequences of space characters (x20) * * @param value The value to normalize * @return Whether the value was changed or not. */ private boolean normalizeDefaultAttrValue(XMLString value) { int oldLength = value.length; boolean skipSpace = true; // skip leading spaces int current = value.offset; int end = value.offset + value.length; for (int i = value.offset; i < end; i++) { if (value.ch[i] == ' ') { if (!skipSpace) { // take the first whitespace as a space and skip the others value.ch[current++] = ' '; skipSpace = true; } else { // just skip it. } } else { // simply shift non space chars if needed if (current != i) { value.ch[current] = value.ch[i]; } current++; skipSpace = false; } } if (current != end) { if (skipSpace) { // if we finished on a space trim it current--; } // set the new value length value.length = current - value.offset; return true; } return false; } public void endDTD(Augmentations augs) throws XNIException { } }