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