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. */
DTDGrammarUtil(SymbolTable symbolTable)104     public DTDGrammarUtil(SymbolTable symbolTable) {
105         fSymbolTable = symbolTable;
106     }
107 
DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable)108     public DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable) {
109         fDTDGrammar = grammar;
110         fSymbolTable = symbolTable;
111     }
112 
DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable, NamespaceContext namespaceContext)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      */
reset(XMLComponentManager componentManager)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      */
startElement(QName element, XMLAttributes attributes)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      */
endElement(QName element)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      */
startCDATA(Augmentations augs)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      */
endCDATA(Augmentations augs)188     public void endCDATA(Augmentations augs) throws XNIException {
189     }
190 
191 
192 
193     /** Add default attributes and validate. */
addDTDDefaultAttrs(QName elementName, XMLAttributes attributes)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      */
normalizeAttrValue(XMLAttributes attributes, int index)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 */
getAttributeTypeName(XMLAttributeDecl attrDecl)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 */
ensureStackCapacity(int newElementDepth)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      */
handleStartElement(QName element, XMLAttributes attributes)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. */
handleEndElement(QName 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 
isInElementContent()483     public boolean isInElementContent() {
484         return fInElementContent;
485     }
486 
isIgnorableWhiteSpace(XMLString text)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 }
499