001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 1999,2000 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation (http://www.apache.org/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Xerces" and "Apache Software Foundation" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation and was
052: * originally based on software copyright (c) 1999, International
053: * Business Machines, Inc., http://www.apache.org. For more
054: * information on the Apache Software Foundation, please see
055: * <http://www.apache.org/>.
056: */
057:
058: package org.apache.xerces.framework;
059:
060: import org.apache.xerces.utils.StringPool;
061:
062: /**
063: * ContentSpec really exists to aid the parser classes in implementing
064: * access to the grammar.
065: * <p>
066: * This class is used by the DTD scanner and the validator classes,
067: * allowing them to be used separately or together. This "struct"
068: * class is used to build content models for validation, where it
069: * is more efficient to fetch all of the information for each of
070: * these content model "fragments" than to fetch each field one at
071: * a time. Since configurations are allowed to have validators
072: * without a DTD scanner (i.e. a schema validator) and a DTD scanner
073: * without a validator (non-validating processor), this class can be
074: * used by each without requiring the presence of the other.
075: * <p>
076: * When processing element declarations, the DTD scanner will build
077: * up a representation of the content model using the node types that
078: * are defined here. Since a non-validating processor only needs to
079: * remember the type of content model declared (i.e. ANY, EMPTY, MIXED,
080: * or CHILDREN), it is free to discard the specific details of the
081: * MIXED and CHILDREN content models described using this class.
082: * <p>
083: * In the typical case of a validating processor reading the grammar
084: * of the document from a DTD, the information about the content model
085: * declared will be preserved and later "compiled" into an efficient
086: * form for use during element validation. Each content spec node
087: * that is saved is assigned a unique index that is used as a handle
088: * for the "value" or "otherValue" fields of other content spec nodes.
089: * A leaf node has a "value" that is either an index in the string
090: * pool of the element type of that leaf, or a value of -1 to indicate
091: * the special "#PCDATA" leaf type used in a mixed content model.
092: * <p>
093: * For a mixed content model, the content spec will be made up of
094: * leaf and choice content spec nodes, with an optional "zero or more"
095: * node. For example, the mixed content declaration "(#PCDATA)" would
096: * contain a single leaf node with a node value of -1. A mixed content
097: * declaration of "(#PCDATA|foo)*" would have a content spec consisting
098: * of two leaf nodes, for the "#PCDATA" and "foo" choices, a choice node
099: * with the "value" set to the index of the "#PCDATA" leaf node and the
100: * "otherValue" set to the index of the "foo" leaf node, and a "zero or
101: * more" node with the "value" set to the index of the choice node. If
102: * the content model has more choices, for example "(#PCDATA|a|b)*", then
103: * there will be more corresponding choice and leaf nodes, the choice
104: * nodes will be chained together through the "value" field with each
105: * leaf node referenced by the "otherValue" field.
106: * <p>
107: * For element content models, there are sequence nodes and also "zero or
108: * one" and "one or more" nodes. The leaf nodes would always have a valid
109: * string pool index, as the "#PCDATA" leaf is not used in the declarations
110: * for element content models.
111: *
112: * @version $Id: XMLContentSpec.java,v 1.14 2001/05/31 15:10:02 neilg Exp $
113: */
114: public class XMLContentSpec {
115:
116: //
117: // Constants
118: //
119:
120: /**
121: * Name or #PCDATA. Leaf nodes that represent parsed character
122: * data (#PCDATA) have values of -1.
123: */
124: public static final int CONTENTSPECNODE_LEAF = 0;
125:
126: /** Represents a zero or one occurence count, '?'. */
127: public static final int CONTENTSPECNODE_ZERO_OR_ONE = 1;
128:
129: /** Represents a zero or more occurence count, '*'. */
130: public static final int CONTENTSPECNODE_ZERO_OR_MORE = 2;
131:
132: /** Represents a one or more occurence count, '+'. */
133: public static final int CONTENTSPECNODE_ONE_OR_MORE = 3;
134:
135: /** Represents choice, '|'. */
136: public static final int CONTENTSPECNODE_CHOICE = 4;
137:
138: /** Represents sequence, ','. */
139: public static final int CONTENTSPECNODE_SEQ = 5;
140:
141: /**
142: * Represents any namespace specified namespace. When the element
143: * found in the document must belong to a specific namespace,
144: * <code>otherValue</code> will contain the name of the namespace.
145: * If <code>otherValue</code> is <code>-1</code> then the element
146: * can be from any namespace.
147: * <p>
148: * Lists of valid namespaces are created from choice content spec
149: * nodes that have any content spec nodes as children.
150: */
151: public static final int CONTENTSPECNODE_ANY = 6;
152:
153: /**
154: * Represents any other namespace (XML Schema: ##other).
155: * <p>
156: * When the content spec node type is set to CONTENTSPECNODE_ANY_OTHER,
157: * <code>value</code> will contain the namespace that <em>cannot</em>
158: * occur.
159: */
160: public static final int CONTENTSPECNODE_ANY_OTHER = 7;
161:
162: /** Represents any namespace element (including "##local"). */
163: public static final int CONTENTSPECNODE_ANY_NS = 8;
164:
165: /** Represents <ALL> */
166: public static final int CONTENTSPECNODE_ALL = 9;
167:
168: /** prcessContent is 'lax' **/
169: public static final int CONTENTSPECNODE_ANY_LAX = 22;
170:
171: public static final int CONTENTSPECNODE_ANY_OTHER_LAX = 23;
172:
173: public static final int CONTENTSPECNODE_ANY_NS_LAX = 24;
174:
175: /** processContent is 'skip' **/
176:
177: public static final int CONTENTSPECNODE_ANY_SKIP = 38;
178:
179: public static final int CONTENTSPECNODE_ANY_OTHER_SKIP = 39;
180:
181: public static final int CONTENTSPECNODE_ANY_NS_SKIP = 40;
182: //
183: // Data
184: //
185:
186: /**
187: * The content spec node type.
188: *
189: * @see CONTENTSPECNODE_LEAF
190: * @see CONTENTSPECNODE_ZERO_OR_ONE
191: * @see CONTENTSPECNODE_ZERO_OR_MORE
192: * @see CONTENTSPECNODE_ONE_OR_MORE
193: * @see CONTENTSPECNODE_CHOICE
194: * @see CONTENTSPECNODE_SEQ
195: * @see CONTENTSPECNODE_ALL
196: */
197: public int type;
198:
199: /**
200: * The "left hand" value of the content spec node.
201: * // leaf index, single child for unary ops, left child for binary ops.
202: */
203: public int value;
204:
205: /**
206: * The "right hand" value of the content spec node.
207: * // right child for binary ops
208: */
209: public int otherValue;
210:
211: //
212: // Constructors
213: //
214:
215: /** Default constructor. */
216: public XMLContentSpec() {
217: clear();
218: }
219:
220: /** Constructs a content spec with the specified values. */
221: public XMLContentSpec(int type, int value, int otherValue) {
222: setValues(type, value, otherValue);
223: }
224:
225: /**
226: * Constructs a content spec from the values in the specified content spec.
227: */
228: public XMLContentSpec(XMLContentSpec contentSpec) {
229: setValues(contentSpec);
230: }
231:
232: /**
233: * Constructs a content spec from the values specified by the given
234: * content spec provider and identifier.
235: */
236: public XMLContentSpec(XMLContentSpec.Provider provider,
237: int contentSpecIndex) {
238: setValues(provider, contentSpecIndex);
239: }
240:
241: //
242: // Public methods
243: //
244:
245: /** Clears the values. */
246: public void clear() {
247: type = -1;
248: value = -1;
249: otherValue = -1;
250: }
251:
252: /** Sets the values. */
253: public void setValues(int type, int value, int otherValue) {
254: this .type = type;
255: this .value = value;
256: this .otherValue = otherValue;
257: }
258:
259: /** Sets the values of the specified content spec. */
260: public void setValues(XMLContentSpec contentSpec) {
261: type = contentSpec.type;
262: value = contentSpec.value;
263: otherValue = contentSpec.otherValue;
264: }
265:
266: /**
267: * Sets the values from the values specified by the given content spec
268: * provider and identifier. If the specified content spec cannot be
269: * provided, the values of this content spec are cleared.
270: */
271: public void setValues(XMLContentSpec.Provider provider,
272: int contentSpecIndex) {
273: if (!provider.getContentSpec(contentSpecIndex, this )) {
274: clear();
275: }
276: }
277:
278: //
279: // Public static methods
280: //
281:
282: /**
283: * Returns a string representation of the specified content spec
284: * identifier in the form of a DTD element content model.
285: * <p>
286: * <strong>Note:</strong> This method is not namespace aware.
287: */
288: public static String toString(XMLContentSpec.Provider provider,
289: StringPool stringPool, int contentSpecIndex) {
290:
291: // lookup content spec node
292: XMLContentSpec contentSpec = new XMLContentSpec();
293:
294: if (provider.getContentSpec(contentSpecIndex, contentSpec)) {
295:
296: // build string
297: StringBuffer str = new StringBuffer();
298: int parentContentSpecType = contentSpec.type & 0x0f;
299: int nextContentSpec;
300: switch (parentContentSpecType) {
301: case XMLContentSpec.CONTENTSPECNODE_LEAF: {
302: str.append('(');
303: if (contentSpec.value == -1
304: && contentSpec.otherValue == -1) {
305: str.append("#PCDATA");
306: } else {
307: str.append(stringPool.toString(contentSpec.value));
308: }
309: str.append(')');
310: break;
311: }
312: case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE: {
313: provider.getContentSpec(contentSpec.value, contentSpec);
314: nextContentSpec = contentSpec.type;
315:
316: if (nextContentSpec == XMLContentSpec.CONTENTSPECNODE_LEAF) {
317: str.append('(');
318: str.append(stringPool.toString(contentSpec.value));
319: str.append(')');
320: } else if (nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE
321: || nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
322: || nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE) {
323: str.append('(');
324: appendContentSpec(provider, stringPool,
325: contentSpec, str, true,
326: parentContentSpecType);
327: str.append(')');
328:
329: } else {
330: appendContentSpec(provider, stringPool,
331: contentSpec, str, true,
332: parentContentSpecType);
333: }
334: str.append('?');
335: break;
336: }
337: case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE: {
338: provider.getContentSpec(contentSpec.value, contentSpec);
339: nextContentSpec = contentSpec.type;
340:
341: if (nextContentSpec == XMLContentSpec.CONTENTSPECNODE_LEAF) {
342: str.append('(');
343: if (contentSpec.value == -1
344: && contentSpec.otherValue == -1) {
345: str.append("#PCDATA");
346: } else if (contentSpec.otherValue != -1) {
347: str
348: .append("##any:uri="
349: + stringPool
350: .toString(contentSpec.otherValue));
351: } else if (contentSpec.value == -1) {
352: str.append("##any");
353: } else {
354: appendContentSpec(provider, stringPool,
355: contentSpec, str, true,
356: parentContentSpecType);
357: }
358: str.append(')');
359:
360: } else if (nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE
361: || nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
362: || nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE) {
363: str.append('(');
364: appendContentSpec(provider, stringPool,
365: contentSpec, str, true,
366: parentContentSpecType);
367: str.append(')');
368: } else {
369: appendContentSpec(provider, stringPool,
370: contentSpec, str, true,
371: parentContentSpecType);
372:
373: //str.append(stringPool.toString(contentSpec.value));
374: }
375: str.append('*');
376: break;
377: }
378: case XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE: {
379: provider.getContentSpec(contentSpec.value, contentSpec);
380: nextContentSpec = contentSpec.type;
381:
382: if (nextContentSpec == XMLContentSpec.CONTENTSPECNODE_LEAF) {
383: str.append('(');
384: if (contentSpec.value == -1
385: && contentSpec.otherValue == -1) {
386: str.append("#PCDATA");
387: } else if (contentSpec.otherValue != -1) {
388: str
389: .append("##any:uri="
390: + stringPool
391: .toString(contentSpec.otherValue));
392: } else if (contentSpec.value == -1) {
393: str.append("##any");
394: } else {
395: str.append(stringPool
396: .toString(contentSpec.value));
397: }
398: str.append(')');
399: } else if (nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE
400: || nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
401: || nextContentSpec == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE) {
402: str.append('(');
403: appendContentSpec(provider, stringPool,
404: contentSpec, str, true,
405: parentContentSpecType);
406: str.append(')');
407: } else {
408: appendContentSpec(provider, stringPool,
409: contentSpec, str, true,
410: parentContentSpecType);
411: }
412: str.append('+');
413: break;
414: }
415: case XMLContentSpec.CONTENTSPECNODE_ALL:
416: case XMLContentSpec.CONTENTSPECNODE_CHOICE:
417: case XMLContentSpec.CONTENTSPECNODE_SEQ: {
418: appendContentSpec(provider, stringPool, contentSpec,
419: str, true, parentContentSpecType);
420: break;
421: }
422: case XMLContentSpec.CONTENTSPECNODE_ANY: {
423: str.append("##any");
424: break;
425: }
426: case XMLContentSpec.CONTENTSPECNODE_ANY_OTHER: {
427: str.append("##other:uri=");
428: str.append(stringPool.toString(contentSpec.otherValue));
429: break;
430: }
431: case XMLContentSpec.CONTENTSPECNODE_ANY_NS: {
432: str.append("namespace:uri=");
433: str.append(stringPool.toString(contentSpec.otherValue));
434: break;
435: }
436: default: {
437: str.append("???");
438: }
439:
440: } // switch type
441:
442: // return string
443: return str.toString();
444: }
445:
446: // not found
447: return null;
448:
449: } // toString(XMLContentSpec.Provider):String
450:
451: //
452: // Object methods
453: //
454:
455: /** Returns a hash code for this node. */
456: public int hashCode() {
457: return type << 16 | value << 8 | otherValue;
458: }
459:
460: /** Returns true if the two objects are equal. */
461: public boolean equals(Object object) {
462: if (object != null && object instanceof XMLContentSpec) {
463: XMLContentSpec contentSpec = (XMLContentSpec) object;
464: return type == contentSpec.type
465: && value == contentSpec.value
466: && otherValue == contentSpec.otherValue;
467: }
468: return false;
469: }
470:
471: //
472: // Private static methods
473: //
474:
475: /**
476: * Appends more information to the current string buffer.
477: * <p>
478: * <strong>Note:</strong> This method does <em>not</em> preserve the
479: * contents of the content spec node.
480: */
481: private static void appendContentSpec(
482: XMLContentSpec.Provider provider, StringPool stringPool,
483: XMLContentSpec contentSpec, StringBuffer str,
484: boolean parens, int parentContentSpecType) {
485:
486: int this ContentSpec = contentSpec.type & 0x0f;
487: switch (this ContentSpec) {
488: case XMLContentSpec.CONTENTSPECNODE_LEAF: {
489: if (contentSpec.value == -1 && contentSpec.otherValue == -1) {
490: str.append("#PCDATA");
491: } else if (contentSpec.value == -1
492: && contentSpec.otherValue != -1) {
493: str.append("##any:uri="
494: + stringPool.toString(contentSpec.otherValue));
495: } else if (contentSpec.value == -1) {
496: str.append("##any");
497: } else {
498: str.append(stringPool.toString(contentSpec.value));
499: }
500: break;
501: }
502: case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE: {
503: if (parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE
504: || parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
505: || parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE) {
506: provider.getContentSpec(contentSpec.value, contentSpec);
507: str.append('(');
508: appendContentSpec(provider, stringPool, contentSpec,
509: str, true, this ContentSpec);
510: str.append(')');
511:
512: } else {
513: provider.getContentSpec(contentSpec.value, contentSpec);
514: appendContentSpec(provider, stringPool, contentSpec,
515: str, true, this ContentSpec);
516: }
517: str.append('?');
518: break;
519: }
520: case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE: {
521: if (parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE
522: || parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
523: || parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE) {
524: provider.getContentSpec(contentSpec.value, contentSpec);
525: str.append('(');
526: appendContentSpec(provider, stringPool, contentSpec,
527: str, true, this ContentSpec);
528: str.append(')');
529: } else {
530: provider.getContentSpec(contentSpec.value, contentSpec);
531: appendContentSpec(provider, stringPool, contentSpec,
532: str, true, this ContentSpec);
533: }
534: str.append('*');
535: break;
536: }
537: case XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE: {
538: if (parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE
539: || parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
540: || parentContentSpecType == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE) {
541:
542: str.append('(');
543: provider.getContentSpec(contentSpec.value, contentSpec);
544: appendContentSpec(provider, stringPool, contentSpec,
545: str, true, this ContentSpec);
546: str.append(')');
547: } else {
548: provider.getContentSpec(contentSpec.value, contentSpec);
549: appendContentSpec(provider, stringPool, contentSpec,
550: str, true, this ContentSpec);
551: }
552: str.append('+');
553: break;
554: }
555: case XMLContentSpec.CONTENTSPECNODE_CHOICE:
556: case XMLContentSpec.CONTENTSPECNODE_SEQ:
557: case XMLContentSpec.CONTENTSPECNODE_ALL: {
558: int type = contentSpec.type;
559: if (parens) {
560: if (type == XMLContentSpec.CONTENTSPECNODE_ALL)
561: str.append("all(");
562: else
563: str.append('(');
564: }
565: int otherValue = contentSpec.otherValue;
566: provider.getContentSpec(contentSpec.value, contentSpec);
567: appendContentSpec(provider, stringPool, contentSpec, str,
568: contentSpec.type != type, this ContentSpec);
569: if (otherValue != -2) {
570: if (type == XMLContentSpec.CONTENTSPECNODE_CHOICE) {
571: str.append('|');
572: } else {
573: str.append(',');
574: }
575: /***
576: // REVISIT: Do we need this? -Ac
577: if (++index == CHUNK_SIZE) {
578: chunk++;
579: index = 0;
580: }
581: /***/
582: provider.getContentSpec(otherValue, contentSpec);
583: appendContentSpec(provider, stringPool, contentSpec,
584: str, true, this ContentSpec);
585: }
586: if (parens) {
587: str.append(')');
588: }
589: break;
590: }
591: case XMLContentSpec.CONTENTSPECNODE_ANY: {
592: str.append("##any");
593: break;
594: }
595: case XMLContentSpec.CONTENTSPECNODE_ANY_OTHER: {
596: str.append("##other:uri=");
597: str.append(stringPool.toString(contentSpec.otherValue));
598: break;
599: }
600: case XMLContentSpec.CONTENTSPECNODE_ANY_NS: {
601: str.append("namespace:uri=");
602: str.append(stringPool.toString(contentSpec.otherValue));
603: break;
604: }
605: default: {
606: str.append("???");
607: break;
608: }
609:
610: } // switch type
611:
612: } // appendContentSpec(XMLContentSpec.Provider,StringPool,XMLContentSpec,StringBuffer,boolean)
613:
614: //
615: // Interfaces
616: //
617:
618: /**
619: * Provides a means for walking the structure built out of
620: * content spec "nodes". The user of this provider interface is
621: * responsible for knowing what the content spec node values
622: * "mean". If those values refer to content spec identifiers,
623: * then the user can call back into the provider to get the
624: * next content spec node in the structure.
625: */
626: public interface Provider {
627:
628: //
629: // XMLContentSpec.Provider methods
630: //
631:
632: /**
633: * Fills in the provided content spec structure with content spec
634: * information for a unique identifier.
635: *
636: * @param contentSpecIndex The content spec identifier. All content
637: * spec "nodes" have a unique identifier.
638: * @param contentSpec The content spec struct to fill in with
639: * the information.
640: *
641: * @return Returns true if the contentSpecIndex was found.
642: */
643: public boolean getContentSpec(int contentSpecIndex,
644: XMLContentSpec contentSpec);
645:
646: } // interface Provider
647:
648: } // class XMLContentSpec
|