001: /*
002: * $Id: DTDGrammarUtil.java,v 1.4 2006/05/09 12:32:04 sunithareddy Exp $
003: */
004:
005: /*
006: * The contents of this file are subject to the terms
007: * of the Common Development and Distribution License
008: * (the License). You may not use this file except in
009: * compliance with the License.
010: *
011: * You can obtain a copy of the license at
012: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
013: * See the License for the specific language governing
014: * permissions and limitations under the License.
015: *
016: * When distributing Covered Code, include this CDDL
017: * Header Notice in each file and include the License file
018: * at https://glassfish.dev.java.net/public/CDDLv1.0.html.
019: * If applicable, add the following below the CDDL Header,
020: * with the fields enclosed by brackets [] replaced by
021: * you own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * [Name of File] [ver.__] [Date]
025: *
026: * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
027: */
028:
029: /*
030: * The Apache Software License, Version 1.1
031: *
032: *
033: * Copyright (c) 1999-2002 The Apache Software Foundation.
034: * All rights reserved.
035: *
036: * Redistribution and use in source and binary forms, with or without
037: * modification, are permitted provided that the following conditions
038: * are met:
039: *
040: * 1. Redistributions of source code must retain the above copyright
041: * notice, this list of conditions and the following disclaimer.
042: *
043: * 2. Redistributions in binary form must reproduce the above copyright
044: * notice, this list of conditions and the following disclaimer in
045: * the documentation and/or other materials provided with the
046: * distribution.
047: *
048: * 3. The end-user documentation included with the redistribution,
049: * if any, must include the following acknowledgment:
050: * "This product includes software developed by the
051: * Apache Software Foundation (http://www.apache.org/)."
052: * Alternately, this acknowledgment may appear in the software itself,
053: * if and wherever such third-party acknowledgments normally appear.
054: *
055: * 4. The names "Xerces" and "Apache Software Foundation" must
056: * not be used to endorse or promote products derived from this
057: * software without prior written permission. For written
058: * permission, please contact apache@apache.org.
059: *
060: * 5. Products derived from this software may not be called "Apache",
061: * nor may "Apache" appear in their name, without prior written
062: * permission of the Apache Software Foundation.
063: *
064: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
065: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
066: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
067: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
068: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
069: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
070: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
071: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
072: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
073: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
074: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
075: * SUCH DAMAGE.
076: * ====================================================================
077: *
078: * This software consists of voluntary contributions made by many
079: * individuals on behalf of the Apache Software Foundation and was
080: * originally based on software copyright (c) 1999, International
081: * Business Machines, Inc., http://www.apache.org. For more
082: * information on the Apache Software Foundation, please see
083: * <http://www.apache.org/>.
084: *
085: * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
086: * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
087: */
088: package com.sun.xml.stream.dtd;
089:
090: import com.sun.xml.stream.dtd.nonvalidating.*;
091: import com.sun.xml.stream.Constants;
092: import com.sun.xml.stream.XMLEntityManager;
093: import com.sun.xml.stream.XMLErrorReporter;
094: import com.sun.xml.stream.xerces.util.SymbolTable;
095: import com.sun.xml.stream.xerces.util.XMLChar;
096: import com.sun.xml.stream.xerces.util.XMLSymbols;
097: import com.sun.xml.stream.xerces.xni.Augmentations;
098: import com.sun.xml.stream.xerces.xni.QName;
099: import com.sun.xml.stream.xerces.xni.NamespaceContext;
100: import com.sun.xml.stream.xerces.xni.XMLAttributes;
101: import com.sun.xml.stream.xerces.xni.XMLDocumentHandler;
102: import com.sun.xml.stream.xerces.xni.XMLLocator;
103: import com.sun.xml.stream.xerces.xni.XMLString;
104: import com.sun.xml.stream.xerces.xni.XNIException;
105: import com.sun.xml.stream.xerces.xni.parser.XMLComponentManager;
106: import com.sun.xml.stream.xerces.xni.parser.XMLConfigurationException;
107: import com.sun.xml.stream.xerces.xni.parser.XMLDocumentSource;
108: import javax.xml.XMLConstants;
109:
110: /*
111: * @author Eric Ye, IBM
112: * @author Andy Clark, IBM
113: * @author Jeffrey Rodriguez IBM
114: * @author Neil Graham, IBM
115: * @author Sunitha Reddy, Sun Microsystems
116: */
117:
118: public class DTDGrammarUtil {
119:
120: /** Property identifier: symbol table. */
121: protected static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX
122: + Constants.SYMBOL_TABLE_PROPERTY;
123:
124: protected static final String NAMESPACES = Constants.SAX_FEATURE_PREFIX
125: + Constants.NAMESPACES_FEATURE;
126:
127: /** Compile to true to debug attributes. */
128: private static final boolean DEBUG_ATTRIBUTES = false;
129:
130: /** Compile to true to debug element children. */
131: private static final boolean DEBUG_ELEMENT_CHILDREN = false;
132:
133: protected DTDGrammar fDTDGrammar = null;
134: /** Namespaces. */
135: protected boolean fNamespaces;
136:
137: /** Symbol table. */
138: protected SymbolTable fSymbolTable = null;
139:
140: /** Current element index. */
141: private int fCurrentElementIndex = -1;
142:
143: /** Current content spec type. */
144: private int fCurrentContentSpecType = -1;
145:
146: private boolean fInCDATASection = false;
147:
148: /** Content spec type stack. */
149: private boolean[] fElementContentState = new boolean[8];
150:
151: /** Element depth. */
152: private int fElementDepth = -1;
153:
154: /** True if inside of element content. */
155: private boolean fInElementContent = false;
156:
157: /** Temporary atribute declaration. */
158: private XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl();
159:
160: /** Temporary qualified name. */
161: private QName fTempQName = new QName();
162:
163: /** Temporary string buffers. */
164: private StringBuffer fBuffer = new StringBuffer();
165:
166: private NamespaceContext fNamespaceContext = null;
167:
168: /** Default constructor. */
169: public DTDGrammarUtil(SymbolTable symbolTable) {
170: fSymbolTable = symbolTable;
171: }
172:
173: public DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable) {
174: fDTDGrammar = grammar;
175: fSymbolTable = symbolTable;
176: }
177:
178: public DTDGrammarUtil(DTDGrammar grammar, SymbolTable symbolTable,
179: NamespaceContext namespaceContext) {
180: fDTDGrammar = grammar;
181: fSymbolTable = symbolTable;
182: fNamespaceContext = namespaceContext;
183: }
184:
185: /*
186: * Resets the component. The component can query the component manager
187: * about any features and properties that affect the operation of the
188: * component.
189: *
190: * @param componentManager The component manager.
191: *
192: * @throws SAXException Thrown by component on finitialization error.
193: * For example, if a feature or property is
194: * required for the operation of the component, the
195: * component manager may throw a
196: * SAXNotRecognizedException or a
197: * SAXNotSupportedException.
198: */
199: public void reset(XMLComponentManager componentManager)
200: throws XMLConfigurationException {
201:
202: fDTDGrammar = null;
203: fInCDATASection = false;
204: fInElementContent = false;
205: fCurrentElementIndex = -1;
206: fCurrentContentSpecType = -1;
207: try {
208: fNamespaces = componentManager.getFeature(NAMESPACES);
209: } catch (XMLConfigurationException e) {
210: fNamespaces = true;
211: }
212: fSymbolTable = (SymbolTable) componentManager
213: .getProperty(Constants.XERCES_PROPERTY_PREFIX
214: + Constants.SYMBOL_TABLE_PROPERTY);
215: fElementDepth = -1;
216: }
217:
218: /**
219: * The start of an element.
220: *
221: * @param element The name of the element.
222: * @param attributes The element attributes.
223: * @param augs Additional information that may include infoset augmentations
224: *
225: * @throws XNIException Thrown by handler to signal an error.
226: */
227: public void startElement(QName element, XMLAttributes attributes)
228: throws XNIException {
229: handleStartElement(element, attributes);
230: }
231:
232: /**
233: * The end of an element.
234: *
235: * @param element The name of the element.
236: * @param augs Additional information that may include infoset augmentations
237: *
238: * @throws XNIException Thrown by handler to signal an error.
239: */
240: public void endElement(QName element) throws XNIException {
241: handleEndElement(element);
242: }
243:
244: /**
245: * The start of a CDATA section.
246: * @param augs Additional information that may include infoset augmentations
247: *
248: * @throws XNIException Thrown by handler to signal an error.
249: */
250: public void startCDATA(Augmentations augs) throws XNIException {
251: fInCDATASection = true;
252: }
253:
254: /**
255: * The end of a CDATA section.
256: * @param augs Additional information that may include infoset augmentations
257: *
258: * @throws XNIException Thrown by handler to signal an error.
259: */
260: public void endCDATA(Augmentations augs) throws XNIException {
261: fInCDATASection = false;
262: }
263:
264: /** Add default attributes and validate. */
265: public void addDTDDefaultAttrs(QName elementName,
266: XMLAttributes attributes) throws XNIException {
267:
268: int elementIndex;
269: elementIndex = fDTDGrammar.getElementDeclIndex(elementName);
270: // is there anything to do?
271: if (elementIndex == -1 || fDTDGrammar == null) {
272: return;
273: }
274:
275: //
276: // Check after all specified attrs are scanned
277: // (1) report error for REQUIRED attrs that are missing (V_TAGc)
278: // (2) add default attrs (FIXED and NOT_FIXED)
279: //
280: int attlistIndex = fDTDGrammar
281: .getFirstAttributeDeclIndex(elementIndex);
282:
283: while (attlistIndex != -1) {
284:
285: fDTDGrammar.getAttributeDecl(attlistIndex, fTempAttDecl);
286:
287: if (DEBUG_ATTRIBUTES) {
288: if (fTempAttDecl != null) {
289: XMLElementDecl elementDecl = new XMLElementDecl();
290: fDTDGrammar.getElementDecl(elementIndex,
291: elementDecl);
292: System.out.println("element: "
293: + (elementDecl.name.localpart));
294: System.out.println("attlistIndex " + attlistIndex
295: + "\n" + "attName : '"
296: + (fTempAttDecl.name.localpart) + "'\n"
297: + "attType : "
298: + fTempAttDecl.simpleType.type + "\n"
299: + "attDefaultType : "
300: + fTempAttDecl.simpleType.defaultType
301: + "\n" + "attDefaultValue : '"
302: + fTempAttDecl.simpleType.defaultValue
303: + "'\n" + attributes.getLength() + "\n");
304: }
305: }
306: String attPrefix = fTempAttDecl.name.prefix;
307: String attLocalpart = fTempAttDecl.name.localpart;
308: String attRawName = fTempAttDecl.name.rawname;
309: String attType = getAttributeTypeName(fTempAttDecl);
310:
311: int attDefaultType = fTempAttDecl.simpleType.defaultType;
312: String attValue = null;
313:
314: if (fTempAttDecl.simpleType.defaultValue != null) {
315: attValue = fTempAttDecl.simpleType.defaultValue;
316: }
317: boolean specified = false;
318: boolean required = attDefaultType == XMLSimpleType.DEFAULT_TYPE_REQUIRED;
319: boolean cdata = attType == XMLSymbols.fCDATASymbol;
320:
321: if (!cdata || required || attValue != null) {
322:
323: //check whether attribute is a namespace declaration
324: if (fNamespaceContext != null
325: && attRawName
326: .startsWith(XMLConstants.XMLNS_ATTRIBUTE)) {
327: String prefix = "";
328: int pos = attRawName.indexOf(':');
329: if (pos != -1) {
330: prefix = attRawName.substring(0, pos);
331: } else {
332: prefix = attRawName;
333: }
334: prefix = fSymbolTable.addSymbol(prefix);
335: if (!((com.sun.xml.stream.xerces.util.NamespaceSupport) fNamespaceContext)
336: .containsPrefixInCurrentContext(prefix)) {
337: fNamespaceContext.declarePrefix(prefix,
338: attValue);
339: }
340: specified = true;
341: } else {
342: int attrCount = attributes.getLength();
343: for (int i = 0; i < attrCount; i++) {
344: if (attributes.getQName(i) == attRawName) {
345: specified = true;
346: break;
347: }
348: }
349: }
350:
351: }
352:
353: if (!specified) {
354: if (attValue != null) {
355: if (fNamespaces) {
356: int index = attRawName.indexOf(':');
357: if (index != -1) {
358: attPrefix = attRawName.substring(0, index);
359: attPrefix = fSymbolTable
360: .addSymbol(attPrefix);
361: attLocalpart = attRawName
362: .substring(index + 1);
363: attLocalpart = fSymbolTable
364: .addSymbol(attLocalpart);
365: }
366: }
367: fTempQName.setValues(attPrefix, attLocalpart,
368: attRawName, fTempAttDecl.name.uri);
369: int newAttr = attributes.addAttribute(fTempQName,
370: attType, attValue);
371: }
372: }
373: attlistIndex = fDTDGrammar
374: .getNextAttributeDeclIndex(attlistIndex);
375: }
376:
377: // now iterate through the expanded attributes for
378: // 1. if every attribute seen is declared in the DTD
379: // 2. check if the VC: default_fixed holds
380: // 3. validate every attribute.
381: int attrCount = attributes.getLength();
382: for (int i = 0; i < attrCount; i++) {
383: String attrRawName = attributes.getQName(i);
384: boolean declared = false;
385: int attDefIndex = -1;
386: int position = fDTDGrammar
387: .getFirstAttributeDeclIndex(elementIndex);
388: while (position != -1) {
389: fDTDGrammar.getAttributeDecl(position, fTempAttDecl);
390: if (fTempAttDecl.name.rawname == attrRawName) {
391: // found the match att decl,
392: attDefIndex = position;
393: declared = true;
394: break;
395: }
396: position = fDTDGrammar
397: .getNextAttributeDeclIndex(position);
398: }
399: if (!declared) {
400: continue;
401: }
402:
403: String type = getAttributeTypeName(fTempAttDecl);
404: attributes.setType(i, type);
405:
406: boolean changedByNormalization = false;
407: String oldValue = attributes.getValue(i);
408: String attrValue = oldValue;
409: if (attributes.isSpecified(i)
410: && type != XMLSymbols.fCDATASymbol) {
411: changedByNormalization = normalizeAttrValue(attributes,
412: i);
413: attrValue = attributes.getValue(i);
414: }
415: } // for all attributes
416:
417: } // addDTDDefaultAttrsAndValidate(int,XMLAttrList)
418:
419: /**
420: * Normalize the attribute value of a non CDATA attributes collapsing
421: * sequences of space characters (x20)
422: *
423: * @param attributes The list of attributes
424: * @param index The index of the attribute to normalize
425: */
426: private boolean normalizeAttrValue(XMLAttributes attributes,
427: int index) {
428: // vars
429: boolean leadingSpace = true;
430: boolean spaceStart = false;
431: boolean readingNonSpace = false;
432: int count = 0;
433: int eaten = 0;
434: String attrValue = attributes.getValue(index);
435: char[] attValue = new char[attrValue.length()];
436:
437: fBuffer.setLength(0);
438: attrValue.getChars(0, attrValue.length(), attValue, 0);
439: for (int i = 0; i < attValue.length; i++) {
440:
441: if (attValue[i] == ' ') {
442:
443: // now the tricky part
444: if (readingNonSpace) {
445: spaceStart = true;
446: readingNonSpace = false;
447: }
448:
449: if (spaceStart && !leadingSpace) {
450: spaceStart = false;
451: fBuffer.append(attValue[i]);
452: count++;
453: } else {
454: if (leadingSpace || !spaceStart) {
455: eaten++;
456: }
457: }
458:
459: } else {
460: readingNonSpace = true;
461: spaceStart = false;
462: leadingSpace = false;
463: fBuffer.append(attValue[i]);
464: count++;
465: }
466: }
467:
468: // check if the last appended character is a space.
469: if (count > 0 && fBuffer.charAt(count - 1) == ' ') {
470: fBuffer.setLength(count - 1);
471:
472: }
473: String newValue = fBuffer.toString();
474: attributes.setValue(index, newValue);
475: return !attrValue.equals(newValue);
476: }
477:
478: /** convert attribute type from ints to strings */
479: private String getAttributeTypeName(XMLAttributeDecl attrDecl) {
480:
481: switch (attrDecl.simpleType.type) {
482: case XMLSimpleType.TYPE_ENTITY: {
483: return attrDecl.simpleType.list ? XMLSymbols.fENTITIESSymbol
484: : XMLSymbols.fENTITYSymbol;
485: }
486: case XMLSimpleType.TYPE_ENUMERATION: {
487: StringBuffer buffer = new StringBuffer();
488: buffer.append('(');
489: for (int i = 0; i < attrDecl.simpleType.enumeration.length; i++) {
490: if (i > 0) {
491: buffer.append("|");
492: }
493: buffer.append(attrDecl.simpleType.enumeration[i]);
494: }
495: buffer.append(')');
496: return fSymbolTable.addSymbol(buffer.toString());
497: }
498: case XMLSimpleType.TYPE_ID: {
499: return XMLSymbols.fIDSymbol;
500: }
501: case XMLSimpleType.TYPE_IDREF: {
502: return attrDecl.simpleType.list ? XMLSymbols.fIDREFSSymbol
503: : XMLSymbols.fIDREFSymbol;
504: }
505: case XMLSimpleType.TYPE_NMTOKEN: {
506: return attrDecl.simpleType.list ? XMLSymbols.fNMTOKENSSymbol
507: : XMLSymbols.fNMTOKENSymbol;
508: }
509: case XMLSimpleType.TYPE_NOTATION: {
510: return XMLSymbols.fNOTATIONSymbol;
511: }
512: }
513: return XMLSymbols.fCDATASymbol;
514:
515: }
516:
517: /** ensure element stack capacity */
518: private void ensureStackCapacity(int newElementDepth) {
519: if (newElementDepth == fElementContentState.length) {
520: boolean[] newStack = new boolean[newElementDepth * 2];
521: System.arraycopy(this .fElementContentState, 0, newStack, 0,
522: newElementDepth);
523: fElementContentState = newStack;
524: }
525: }
526:
527: /** Handle element
528: * @return true if validator is removed from the pipeline
529: */
530: protected void handleStartElement(QName element,
531: XMLAttributes attributes) throws XNIException {
532:
533: if (fDTDGrammar == null) {
534: fCurrentElementIndex = -1;
535: fCurrentContentSpecType = -1;
536: fInElementContent = false;
537: return;
538: } else {
539: fCurrentElementIndex = fDTDGrammar
540: .getElementDeclIndex(element);
541: fCurrentContentSpecType = fDTDGrammar
542: .getContentSpecType(fCurrentElementIndex);
543: //handleDTDDefaultAttrs(element,attributes);
544: addDTDDefaultAttrs(element, attributes);
545: }
546:
547: fInElementContent = fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN;
548: fElementDepth++;
549: ensureStackCapacity(fElementDepth);
550: fElementContentState[fElementDepth] = fInElementContent;
551: }
552:
553: /** Handle end element. */
554: protected void handleEndElement(QName element) throws XNIException {
555: fElementDepth--;
556: if (fElementDepth < -1) {
557: throw new RuntimeException("FWK008 Element stack underflow");
558: }
559: if (fElementDepth < 0) {
560: fCurrentElementIndex = -1;
561: fCurrentContentSpecType = -1;
562: fInElementContent = false;
563: return;
564: }
565: fInElementContent = fElementContentState[fElementDepth];
566: }
567:
568: public boolean isInElementContent() {
569: return fInElementContent;
570: }
571:
572: public boolean isIgnorableWhiteSpace(XMLString text) {
573: if (isInElementContent()) {
574: for (int i = text.offset; i < text.offset + text.length; i++) {
575: if (!XMLChar.isSpace(text.ch[i])) {
576: return false;
577: }
578: }
579: return true;
580: }
581: return false;
582: }
583: }
|