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
22package com.sun.xml.internal.stream.dtd;
23import com.sun.xml.internal.stream.dtd.nonvalidating.DTDGrammar;
24import com.sun.xml.internal.stream.dtd.nonvalidating.XMLAttributeDecl;
25import com.sun.xml.internal.stream.dtd.nonvalidating.XMLElementDecl;
26import com.sun.xml.internal.stream.dtd.nonvalidating.XMLSimpleType;
27import com.sun.org.apache.xerces.internal.impl.Constants;
28import com.sun.org.apache.xerces.internal.util.SymbolTable;
29import com.sun.org.apache.xerces.internal.util.XMLChar;
30import com.sun.org.apache.xerces.internal.util.XMLSymbols;
31import com.sun.org.apache.xerces.internal.xni.Augmentations;
32import com.sun.org.apache.xerces.internal.xni.QName;
33import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
34import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
35import com.sun.org.apache.xerces.internal.xni.XMLString;
36import com.sun.org.apache.xerces.internal.xni.XNIException;
37import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
38import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
39import 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
49public 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. */
100    public DTDGrammarUtil(SymbolTable symbolTable) {
101        fSymbolTable = symbolTable;
102    }
103
104    public DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable) {
105        fDTDGrammar = grammar;
106        fSymbolTable = symbolTable;
107    }
108
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     */
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     */
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     */
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     */
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     */
184    public void endCDATA(Augmentations augs) throws XNIException {
185    }
186
187
188
189    /** Add default attributes and validate. */
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     */
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 */
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 */
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     */
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. */
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
479    public boolean isInElementContent() {
480        return fInElementContent;
481    }
482
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