1 /* 2 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. 3 */ 4 5 /* 6 * Copyright 2005 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.xml.internal.stream.dtd; 22 import com.sun.xml.internal.stream.dtd.nonvalidating.DTDGrammar; 23 import com.sun.xml.internal.stream.dtd.nonvalidating.XMLAttributeDecl; 24 import com.sun.xml.internal.stream.dtd.nonvalidating.XMLElementDecl; 25 import com.sun.xml.internal.stream.dtd.nonvalidating.XMLSimpleType; 26 import com.sun.org.apache.xerces.internal.impl.Constants; 27 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager; 28 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; 29 import com.sun.org.apache.xerces.internal.util.SymbolTable; 30 import com.sun.org.apache.xerces.internal.util.XMLChar; 31 import com.sun.org.apache.xerces.internal.util.XMLSymbols; 32 import com.sun.org.apache.xerces.internal.xni.Augmentations; 33 import com.sun.org.apache.xerces.internal.xni.QName; 34 import com.sun.org.apache.xerces.internal.xni.NamespaceContext; 35 import com.sun.org.apache.xerces.internal.xni.XMLAttributes; 36 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; 37 import com.sun.org.apache.xerces.internal.xni.XMLLocator; 38 import com.sun.org.apache.xerces.internal.xni.XMLString; 39 import com.sun.org.apache.xerces.internal.xni.XNIException; 40 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; 41 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 42 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource; 43 import javax.xml.XMLConstants; 44 45 /* 46 * @author Eric Ye, IBM 47 * @author Andy Clark, IBM 48 * @author Jeffrey Rodriguez IBM 49 * @author Neil Graham, IBM 50 * @author Sunitha Reddy, Sun Microsystems 51 */ 52 53 public class DTDGrammarUtil { 54 55 56 /** Property identifier: symbol table. */ 57 protected static final String SYMBOL_TABLE = 58 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; 59 60 protected static final String NAMESPACES = 61 Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE; 62 63 64 /** Compile to true to debug attributes. */ 65 private static final boolean DEBUG_ATTRIBUTES = false; 66 67 /** Compile to true to debug element children. */ 68 private static final boolean DEBUG_ELEMENT_CHILDREN = false; 69 70 protected DTDGrammar fDTDGrammar = null; 71 /** Namespaces. */ 72 protected boolean fNamespaces; 73 74 /** Symbol table. */ 75 protected SymbolTable fSymbolTable = null; 76 77 /** Current element index. */ 78 private int fCurrentElementIndex = -1; 79 80 /** Current content spec type. */ 81 private int fCurrentContentSpecType = -1; 82 83 /** Content spec type stack. */ 84 private boolean[] fElementContentState = new boolean[8]; 85 86 /** Element depth. */ 87 private int fElementDepth = -1; 88 89 /** True if inside of element content. */ 90 private boolean fInElementContent = false; 91 92 /** Temporary atribute declaration. */ 93 private XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl(); 94 95 /** Temporary qualified name. */ 96 private QName fTempQName = new QName(); 97 98 /** Temporary string buffers. */ 99 private StringBuffer fBuffer = new StringBuffer(); 100 101 private NamespaceContext fNamespaceContext = null; 102 103 /** Default constructor. */ 104 public DTDGrammarUtil(SymbolTable symbolTable) { 105 fSymbolTable = symbolTable; 106 } 107 108 public DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable) { 109 fDTDGrammar = grammar; 110 fSymbolTable = symbolTable; 111 } 112 113 public DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable, 114 NamespaceContext namespaceContext) { 115 fDTDGrammar = grammar; 116 fSymbolTable = symbolTable; 117 fNamespaceContext = namespaceContext; 118 } 119 120 /* 121 * Resets the component. The component can query the component manager 122 * about any features and properties that affect the operation of the 123 * component. 124 * 125 * @param componentManager The component manager. 126 * 127 * @throws SAXException Thrown by component on finitialization error. 128 * For example, if a feature or property is 129 * required for the operation of the component, the 130 * component manager may throw a 131 * SAXNotRecognizedException or a 132 * SAXNotSupportedException. 133 */ 134 public void reset(XMLComponentManager componentManager) 135 throws XMLConfigurationException { 136 137 fDTDGrammar = null; 138 fInElementContent = false; 139 fCurrentElementIndex = -1; 140 fCurrentContentSpecType = -1; 141 fNamespaces = componentManager.getFeature(NAMESPACES, true); 142 fSymbolTable = (SymbolTable) componentManager.getProperty( 143 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY); 144 fElementDepth = -1; 145 } 146 147 148 /** 149 * The start of an element. 150 * 151 * @param element The name of the element. 152 * @param attributes The element attributes. 153 * @param augs Additional information that may include infoset augmentations 154 * 155 * @throws XNIException Thrown by handler to signal an error. 156 */ 157 public void startElement(QName element, XMLAttributes attributes) throws XNIException { 158 handleStartElement(element, attributes); 159 } 160 161 /** 162 * The end of an element. 163 * 164 * @param element The name of the element. 165 * @param augs Additional information that may include infoset augmentations 166 * 167 * @throws XNIException Thrown by handler to signal an error. 168 */ 169 public void endElement(QName element) throws XNIException { 170 handleEndElement(element); 171 } 172 173 /** 174 * The start of a CDATA section. 175 * @param augs Additional information that may include infoset augmentations 176 * 177 * @throws XNIException Thrown by handler to signal an error. 178 */ 179 public void startCDATA(Augmentations augs) throws XNIException { 180 } 181 182 /** 183 * The end of a CDATA section. 184 * @param augs Additional information that may include infoset augmentations 185 * 186 * @throws XNIException Thrown by handler to signal an error. 187 */ 188 public void endCDATA(Augmentations augs) throws XNIException { 189 } 190 191 192 193 /** Add default attributes and validate. */ 194 public void addDTDDefaultAttrs(QName elementName, XMLAttributes attributes) 195 throws XNIException { 196 197 int elementIndex; 198 elementIndex = fDTDGrammar.getElementDeclIndex(elementName); 199 // is there anything to do? 200 if (elementIndex == -1 || fDTDGrammar == null) { 201 return; 202 } 203 204 // 205 // Check after all specified attrs are scanned 206 // (1) report error for REQUIRED attrs that are missing (V_TAGc) 207 // (2) add default attrs (FIXED and NOT_FIXED) 208 // 209 int attlistIndex = fDTDGrammar.getFirstAttributeDeclIndex(elementIndex); 210 211 while (attlistIndex != -1) { 212 213 fDTDGrammar.getAttributeDecl(attlistIndex, fTempAttDecl); 214 215 if (DEBUG_ATTRIBUTES) { 216 if (fTempAttDecl != null) { 217 XMLElementDecl elementDecl = new XMLElementDecl(); 218 fDTDGrammar.getElementDecl(elementIndex, elementDecl); 219 System.out.println("element: " + (elementDecl.name.localpart)); 220 System.out.println("attlistIndex " + attlistIndex + "\n" + 221 "attName : '" + (fTempAttDecl.name.localpart) + "'\n" 222 + "attType : " + fTempAttDecl.simpleType.type + "\n" 223 + "attDefaultType : " + fTempAttDecl.simpleType.defaultType + "\n" 224 + "attDefaultValue : '" + fTempAttDecl.simpleType.defaultValue + "'\n" 225 + attributes.getLength() + "\n" 226 ); 227 } 228 } 229 String attPrefix = fTempAttDecl.name.prefix; 230 String attLocalpart = fTempAttDecl.name.localpart; 231 String attRawName = fTempAttDecl.name.rawname; 232 String attType = getAttributeTypeName(fTempAttDecl); 233 int attDefaultType = fTempAttDecl.simpleType.defaultType; 234 String attValue = null; 235 236 if (fTempAttDecl.simpleType.defaultValue != null) { 237 attValue = fTempAttDecl.simpleType.defaultValue; 238 } 239 boolean specified = false; 240 boolean required = attDefaultType == XMLSimpleType.DEFAULT_TYPE_REQUIRED; 241 boolean cdata = attType == XMLSymbols.fCDATASymbol; 242 243 if (!cdata || required || attValue != null) { 244 245 //check whether attribute is a namespace declaration 246 if (fNamespaceContext != null && attRawName.startsWith(XMLConstants.XMLNS_ATTRIBUTE)) { 247 String prefix = ""; 248 int pos = attRawName.indexOf(':'); 249 if (pos != -1) { 250 prefix = attRawName.substring(0, pos); 251 } else { 252 prefix = attRawName; 253 } 254 prefix = fSymbolTable.addSymbol(prefix); 255 if (!((com.sun.org.apache.xerces.internal.util. 256 NamespaceSupport) fNamespaceContext). 257 containsPrefixInCurrentContext(prefix)) { 258 fNamespaceContext.declarePrefix(prefix, attValue); 259 } 260 specified = true; 261 } else { 262 263 int attrCount = attributes.getLength(); 264 for (int i = 0; i < attrCount; i++) { 265 if (attributes.getQName(i) == attRawName) { 266 specified = true; 267 break; 268 } 269 } 270 271 } 272 273 } 274 275 if (!specified) { 276 if (attValue != null) { 277 if (fNamespaces) { 278 int index = attRawName.indexOf(':'); 279 if (index != -1) { 280 attPrefix = attRawName.substring(0, index); 281 attPrefix = fSymbolTable.addSymbol(attPrefix); 282 attLocalpart = attRawName.substring(index + 1); 283 attLocalpart = fSymbolTable.addSymbol(attLocalpart); 284 } 285 } 286 fTempQName.setValues(attPrefix, attLocalpart, attRawName, 287 fTempAttDecl.name.uri); 288 int newAttr = attributes.addAttribute(fTempQName, attType, 289 attValue); 290 } 291 } 292 attlistIndex = fDTDGrammar.getNextAttributeDeclIndex(attlistIndex); 293 } 294 295 // now iterate through the expanded attributes for 296 // 1. if every attribute seen is declared in the DTD 297 // 2. check if the VC: default_fixed holds 298 // 3. validate every attribute. 299 int attrCount = attributes.getLength(); 300 for (int i = 0; i < attrCount; i++) { 301 String attrRawName = attributes.getQName(i); 302 boolean declared = false; 303 int position = 304 fDTDGrammar.getFirstAttributeDeclIndex(elementIndex); 305 while (position != -1) { 306 fDTDGrammar.getAttributeDecl(position, fTempAttDecl); 307 if (fTempAttDecl.name.rawname == attrRawName) { 308 // found the match att decl, 309 declared = true; 310 break; 311 } 312 position = fDTDGrammar.getNextAttributeDeclIndex(position); 313 } 314 if (!declared) { 315 continue; 316 } 317 318 String type = getAttributeTypeName(fTempAttDecl); 319 attributes.setType(i, type); 320 321 boolean changedByNormalization = false; 322 if (attributes.isSpecified(i) && type != XMLSymbols.fCDATASymbol) { 323 changedByNormalization = normalizeAttrValue(attributes, i); 324 } 325 } // for all attributes 326 327 } // addDTDDefaultAttrsAndValidate(int,XMLAttrList) 328 329 330 /** 331 * Normalize the attribute value of a non CDATA attributes collapsing 332 * sequences of space characters (x20) 333 * 334 * @param attributes The list of attributes 335 * @param index The index of the attribute to normalize 336 */ 337 private boolean normalizeAttrValue(XMLAttributes attributes, int index) { 338 // vars 339 boolean leadingSpace = true; 340 boolean spaceStart = false; 341 boolean readingNonSpace = false; 342 int count = 0; 343 int eaten = 0; 344 String attrValue = attributes.getValue(index); 345 char[] attValue = new char[attrValue.length()]; 346 347 fBuffer.setLength(0); 348 attrValue.getChars(0, attrValue.length(), attValue, 0); 349 for (int i = 0; i < attValue.length; i++) { 350 351 if (attValue[i] == ' ') { 352 353 // now the tricky part 354 if (readingNonSpace) { 355 spaceStart = true; 356 readingNonSpace = false; 357 } 358 359 if (spaceStart && !leadingSpace) { 360 spaceStart = false; 361 fBuffer.append(attValue[i]); 362 count++; 363 } else { 364 if (leadingSpace || !spaceStart) { 365 eaten++; 366 } 367 } 368 369 } else { 370 readingNonSpace = true; 371 spaceStart = false; 372 leadingSpace = false; 373 fBuffer.append(attValue[i]); 374 count++; 375 } 376 } 377 378 // check if the last appended character is a space. 379 if (count > 0 && fBuffer.charAt(count - 1) == ' ') { 380 fBuffer.setLength(count - 1); 381 382 } 383 String newValue = fBuffer.toString(); 384 attributes.setValue(index, newValue); 385 return !attrValue.equals(newValue); 386 } 387 388 389 390 /** convert attribute type from ints to strings */ 391 private String getAttributeTypeName(XMLAttributeDecl attrDecl) { 392 393 switch (attrDecl.simpleType.type) { 394 case XMLSimpleType.TYPE_ENTITY: { 395 return attrDecl.simpleType.list ? XMLSymbols.fENTITIESSymbol : 396 XMLSymbols.fENTITYSymbol; 397 } 398 case XMLSimpleType.TYPE_ENUMERATION: { 399 StringBuffer buffer = new StringBuffer(); 400 buffer.append('('); 401 for (int i = 0; i < attrDecl.simpleType.enumeration.length; i++) { 402 if (i > 0) { 403 buffer.append("|"); 404 } 405 buffer.append(attrDecl.simpleType.enumeration[i]); 406 } 407 buffer.append(')'); 408 return fSymbolTable.addSymbol(buffer.toString()); 409 } 410 case XMLSimpleType.TYPE_ID: { 411 return XMLSymbols.fIDSymbol; 412 } 413 case XMLSimpleType.TYPE_IDREF: { 414 return attrDecl.simpleType.list ? XMLSymbols.fIDREFSSymbol : 415 XMLSymbols.fIDREFSymbol; 416 } 417 case XMLSimpleType.TYPE_NMTOKEN: { 418 return attrDecl.simpleType.list ? XMLSymbols.fNMTOKENSSymbol : 419 XMLSymbols.fNMTOKENSymbol; 420 } 421 case XMLSimpleType.TYPE_NOTATION: { 422 return XMLSymbols.fNOTATIONSymbol; 423 } 424 } 425 return XMLSymbols.fCDATASymbol; 426 427 } 428 429 430 /** ensure element stack capacity */ 431 private void ensureStackCapacity(int newElementDepth) { 432 if (newElementDepth == fElementContentState.length) { 433 boolean[] newStack = new boolean[newElementDepth * 2]; 434 System.arraycopy(this.fElementContentState, 0, newStack, 0, 435 newElementDepth); 436 fElementContentState = newStack; 437 } 438 } 439 440 441 442 /** Handle element 443 * @return true if validator is removed from the pipeline 444 */ 445 protected void handleStartElement(QName element, XMLAttributes attributes) throws XNIException { 446 447 if (fDTDGrammar == null) { 448 fCurrentElementIndex = -1; 449 fCurrentContentSpecType = -1; 450 fInElementContent = false; 451 return; 452 } else { 453 fCurrentElementIndex = fDTDGrammar.getElementDeclIndex(element); 454 fCurrentContentSpecType = fDTDGrammar.getContentSpecType( 455 fCurrentElementIndex); 456 //handleDTDDefaultAttrs(element,attributes); 457 addDTDDefaultAttrs(element, attributes); 458 } 459 460 fInElementContent = fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN; 461 fElementDepth++; 462 ensureStackCapacity(fElementDepth); 463 fElementContentState[fElementDepth] = fInElementContent; 464 } 465 466 467 /** Handle end element. */ 468 protected void handleEndElement(QName element) throws XNIException { 469 if (fDTDGrammar == null) return; 470 fElementDepth--; 471 if (fElementDepth < -1) { 472 throw new RuntimeException("FWK008 Element stack underflow"); 473 } 474 if (fElementDepth < 0) { 475 fCurrentElementIndex = -1; 476 fCurrentContentSpecType = -1; 477 fInElementContent = false; 478 return; 479 } 480 fInElementContent = fElementContentState[fElementDepth]; 481 } 482 483 public boolean isInElementContent() { 484 return fInElementContent; 485 } 486 487 public boolean isIgnorableWhiteSpace(XMLString text) { 488 if (isInElementContent()) { 489 for (int i = text.offset; i < text.offset + text.length; i++) { 490 if (!XMLChar.isSpace(text.ch[i])) { 491 return false; 492 } 493 } 494 return true; 495 } 496 return false; 497 } 498 }