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