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 }