001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 2001 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: package org.apache.xerces.validators.common;
058:
059: import org.apache.xerces.utils.ImplementationMessages;
060: import org.apache.xerces.utils.QName;
061: import org.apache.xerces.validators.schema.SubstitutionGroupComparator;
062: import org.apache.xerces.utils.StringPool;
063: import org.apache.xerces.validators.schema.SchemaGrammar;
064: import org.apache.xerces.framework.XMLContentSpec;
065:
066: import java.util.Hashtable;
067: import java.util.Vector;
068:
069: public class AllContentModel implements XMLContentModel {
070:
071: private QName fAllElements[] = new QName[10];
072: private boolean fIsOptionalElement[] = new boolean[10];
073: private boolean fHasOptionalContent = false;
074: private boolean fIsMixed = false;
075:
076: private int fNumElements = 0;
077: private int fNumRequired = 0;
078:
079: private Hashtable fElementsHash;
080:
081: /* this is the SubstitutionGroupComparator object */
082: private SubstitutionGroupComparator fComparator = null;
083:
084: /** Set to true to debug content model validation. */
085: private static final boolean DEBUG_VALIDATE_CONTENT = false;
086:
087: /**
088: *
089: */
090: public AllContentModel(boolean hasOptionalContent) {
091: fHasOptionalContent = hasOptionalContent;
092:
093: if (DEBUG_VALIDATE_CONTENT) {
094: System.out
095: .println("Entering AllContentModel#AllContentModel");
096: System.out.println(" this == " + this );
097: System.out.println(" optionalContent == "
098: + hasOptionalContent);
099: }
100: }
101:
102: public AllContentModel(boolean hasOptionalContent, boolean isMixed) {
103: this (hasOptionalContent);
104: fIsMixed = isMixed;
105:
106: if (DEBUG_VALIDATE_CONTENT) {
107: System.out.println(" mixed == " + fIsMixed);
108: }
109: }
110:
111: void addElement(QName newElement, boolean isOptional) {
112: if (DEBUG_VALIDATE_CONTENT) {
113: System.out.println("Entering AllContentModel#addElement");
114: }
115:
116: // Need to resize arrays?
117: if (fNumElements >= fAllElements.length) {
118: QName newAllElements[] = new QName[2 * fAllElements.length];
119: boolean newIsOptionalElements[] = new boolean[2 * fIsOptionalElement.length];
120:
121: System.arraycopy(fAllElements, 0, newAllElements, 0,
122: fAllElements.length);
123: System
124: .arraycopy(fIsOptionalElement, 0,
125: newIsOptionalElements, 0,
126: fIsOptionalElement.length);
127:
128: fAllElements = newAllElements;
129: fIsOptionalElement = newIsOptionalElements;
130: }
131:
132: // Enter the new element into our array
133: fAllElements[fNumElements] = newElement;
134: fIsOptionalElement[fNumElements] = isOptional;
135:
136: fNumElements++;
137:
138: // Allows us to quickly determine whether all
139: // required items were seen during validation
140: if (!isOptional) {
141: fNumRequired++;
142: }
143:
144: if (DEBUG_VALIDATE_CONTENT) {
145: showAllElements();
146: System.out.println("Leaving AllContentModel#addElement");
147: }
148: }
149:
150: // Unique Particle Attribution
151: public void checkUniqueParticleAttribution(SchemaGrammar gram)
152: throws Exception {
153: // rename back
154: for (int i = 0; i < fNumElements; i++)
155: fAllElements[i].uri = gram
156: .getContentSpecOrgUri(fAllElements[i].uri);
157:
158: // check whether there is conflict between any two leaves
159: for (int j = 0; j < fNumElements; j++) {
160: for (int k = j + 1; k < fNumElements; k++) {
161: ElementWildcard.conflict(
162: XMLContentSpec.CONTENTSPECNODE_LEAF,
163: fAllElements[j].localpart, fAllElements[j].uri,
164: XMLContentSpec.CONTENTSPECNODE_LEAF,
165: fAllElements[k].localpart, fAllElements[k].uri,
166: fComparator);
167: }
168: }
169: }
170:
171: // Unique Particle Attribution
172:
173: /**
174: * Check that the specified content is valid according to this
175: * content model. This method can also be called to do 'what if'
176: * testing of content models just to see if they would be valid.
177: * <p>
178: * A value of -1 in the children array indicates a PCDATA node. All other
179: * indexes will be positive and represent child elements. The count can be
180: * zero, since some elements have the EMPTY content model and that must be
181: * confirmed.
182: *
183: * @param children The children of this element. Each integer is an index within
184: * the <code>StringPool</code> of the child element name. An index
185: * of -1 is used to indicate an occurrence of non-whitespace character
186: * data.
187: * @param offset Offset into the array where the children starts.
188: * @param length The number of entries in the <code>children</code> array.
189: *
190: * @return The value -1 if fully valid, else the 0 based index of the child
191: * that first failed. If the value returned is equal to the number
192: * of children, then the specified children are valid but additional
193: * content is required to reach a valid ending state.
194: *
195: * @exception Exception Thrown on error.
196: */
197: public int validateContent(QName children[], int offset, int length)
198: throws Exception {
199:
200: if (DEBUG_VALIDATE_CONTENT)
201: System.out
202: .println("Entering AllContentModel#validateContent");
203:
204: // If <all> had minOccurs of zero and there are
205: // no children to validate, trivially validate
206: if (fHasOptionalContent && length == 0) {
207: if (DEBUG_VALIDATE_CONTENT) {
208: System.out.println("Empty content");
209: System.out
210: .println("Leaving AllContentModel#validateContent");
211: }
212: return -1;
213: }
214:
215: final int numElements = fNumElements;
216:
217: if (fElementsHash == null)
218: createElementsHash();
219:
220: // Check the elements seen for duplicated
221: // elements or elements that aren't permitted.
222: boolean elementSeen[] = new boolean[numElements];
223: int numRequiredSeen = 0;
224:
225: for (int childIndex = 0; childIndex < length; childIndex++) {
226: QName currChild = children[offset + childIndex];
227:
228: // Skip text node if in mixed mode.
229: if (fIsMixed && currChild.localpart == -1)
230: continue;
231:
232: Integer foundIdx = (Integer) fElementsHash.get(currChild);
233:
234: // If this element was not found in the Hashtable of permitted
235: // elements or was seen already, indicate an error was found
236: // at the current index.
237: if (foundIdx == null) {
238: if (DEBUG_VALIDATE_CONTENT) {
239: System.out
240: .println("Unexpected element seen - idx == "
241: + childIndex
242: + " ("
243: + currChild
244: + ")");
245: System.out
246: .println("Leaving AllContentModel#validateContent");
247: }
248:
249: return childIndex;
250: }
251:
252: int foundIdxVal = foundIdx.intValue();
253:
254: // If this element was seen already, indicate an error was
255: // found at the duplicate index.
256: if (elementSeen[foundIdxVal]) {
257: if (DEBUG_VALIDATE_CONTENT) {
258: System.out
259: .println("Duplicate element seen - idx == "
260: + childIndex + " (" + currChild
261: + ")");
262: System.out
263: .println("Leaving AllContentModel#validateContent");
264: }
265:
266: return childIndex;
267: }
268:
269: elementSeen[foundIdxVal] = true;
270:
271: if (!fIsOptionalElement[foundIdxVal]) {
272: numRequiredSeen++;
273: }
274: }
275:
276: // Were all the required elements of the <all> encountered?
277: if (numRequiredSeen != fNumRequired) {
278: if (DEBUG_VALIDATE_CONTENT) {
279: System.out.println("Required element missing");
280: System.out
281: .println("Leaving AllContentModel#validateContent");
282: }
283:
284: return length;
285: }
286:
287: if (DEBUG_VALIDATE_CONTENT) {
288: System.out.println("Successful validation");
289: System.out
290: .println("Leaving AllContentModel#validateContent");
291: }
292:
293: return -1;
294: }
295:
296: /**
297: * This method is different from "validateContent" in that it will try to use
298: * the SubstitutionGroupComparator to match children against the content model.
299: * <p>
300: * A value of -1 in the children array indicates a PCDATA node. All other
301: * indexes will be positive and represent child elements. The count can be
302: * zero, since some elements have the EMPTY content model and that must be
303: * confirmed.
304: *
305: * @param children The children of this element. Each integer is an index within
306: * the <code>StringPool</code> of the child element name. An index
307: * of -1 is used to indicate an occurrence of non-whitespace character
308: * data.
309: * @param offset Offset into the array where the children starts.
310: * @param length The number of entries in the <code>children</code> array.
311: *
312: * @return The value -1 if fully valid, else the 0 based index of the child
313: * that first failed. If the value returned is equal to the number
314: * of children, then the specified children are valid but additional
315: * content is required to reach a valid ending state.
316: *
317: * @exception Exception Thrown on error.
318: */
319: public int validateContentSpecial(QName children[], int offset,
320: int length) throws Exception {
321:
322: if (fComparator == null)
323: return validateContent(children, offset, length);
324:
325: if (DEBUG_VALIDATE_CONTENT)
326: System.out
327: .println("Entering AllContentModel#validateContentSpecial");
328:
329: // If <all> had minOccurs of zero and there are
330: // no children to validate, trivially validate
331: if (fHasOptionalContent && length == 0) {
332: if (DEBUG_VALIDATE_CONTENT) {
333: System.out.println("Empty content");
334: System.out
335: .println("Leaving AllContentModel#validateContentSpecial");
336: }
337: return -1;
338: }
339:
340: final int numElements = fNumElements;
341:
342: // Check the elements seen for duplicated
343: // elements or elements that aren't permitted.
344: boolean elementSeen[] = new boolean[numElements];
345: int numRequiredSeen = 0;
346:
347: childLoop: for (int childIndex = 0; childIndex < length; childIndex++) {
348: QName currChild = children[offset + childIndex];
349:
350: // Skip text node if in mixed mode.
351: if (fIsMixed && currChild.localpart == -1)
352: continue;
353:
354: int compareIdx;
355:
356: for (compareIdx = 0; compareIdx < numElements; compareIdx++) {
357: if (fComparator.isEquivalentTo(currChild,
358: fAllElements[compareIdx])) {
359: // If this element was seen already, indicate an error was
360: // found at the duplicate index.
361: if (elementSeen[compareIdx]) {
362: if (DEBUG_VALIDATE_CONTENT) {
363: System.out
364: .println("Duplicate element seen - idx == "
365: + childIndex
366: + " ("
367: + currChild + ")");
368: System.out
369: .println("Leaving AllContentModel#validateContentSpecial");
370: }
371:
372: return childIndex;
373: }
374:
375: elementSeen[compareIdx] = true;
376:
377: if (!fIsOptionalElement[compareIdx]) {
378: numRequiredSeen++;
379: }
380:
381: // Found this element - go back for the next
382: continue childLoop;
383: }
384: }
385:
386: if (DEBUG_VALIDATE_CONTENT) {
387: System.out.println("Unexpected element seen - idx == "
388: + childIndex + " (" + currChild + ")");
389: System.out
390: .println("Leaving AllContentModel#validateContentSpecial");
391: }
392:
393: // We didn't find the current element in the list of
394: // permitted elements, so report an error at this child
395: return childIndex;
396: }
397:
398: // Were all the required elements of the <all> encountered?
399: if (numRequiredSeen != fNumRequired) {
400: if (DEBUG_VALIDATE_CONTENT) {
401: System.out.println("Required element missing");
402: System.out
403: .println("Leaving AllContentModel#validateContentSpecial");
404: }
405:
406: return length;
407: }
408:
409: if (DEBUG_VALIDATE_CONTENT) {
410: System.out.println("Successful validation");
411: System.out
412: .println("Leaving AllContentModel#validateContentSpecial");
413: }
414:
415: return -1;
416: }
417:
418: /**
419: * The setter method to pass in the SubstitutionGroupComparator.
420: *
421: * @param comparator a SubstitutionGroupComparator object.
422: * @return
423: * @exception
424: */
425: public void setSubstitutionGroupComparator(
426: SubstitutionGroupComparator comparator) {
427: fComparator = comparator;
428: }
429:
430: /**
431: * Returns information about which elements can be placed at a particular point
432: * in the passed element's content model.
433: * <p>
434: * Note that the incoming content model to test must be valid at least up to
435: * the insertion point. If not, then -1 will be returned and the info object
436: * will not have been filled in.
437: * <p>
438: * If, on return, the info.isValidEOC flag is set, then the 'insert after'
439: * element is a valid end of content. In other words, nothing needs to be
440: * inserted after it to make the parent element's content model valid.
441: *
442: * @param fullyValid Only return elements that can be inserted and still
443: * maintain the validity of subsequent elements past the
444: * insertion point (if any). If the insertion point is at
445: * the end, and this is true, then only elements that can
446: * be legal final states will be returned.
447: * @param info An object that contains the required input data for the method,
448: * and which will contain the output information if successful.
449: *
450: * @return The value -1 if fully valid, else the 0 based index of the child
451: * that first failed before the insertion point. If the value
452: * returned is equal to the number of children, then the specified
453: * children are valid but additional content is required to reach a
454: * valid ending state.
455: *
456: * @see InsertableElementsInfo
457: */
458: public int whatCanGoHere(boolean fullyValid,
459: InsertableElementsInfo info) throws Exception {
460:
461: if (DEBUG_VALIDATE_CONTENT)
462: System.out
463: .println("Entering AllContentModel#whatCanGoHere");
464:
465: // ????: How do we deal with "fHasOptionalContent"????
466:
467: if (fElementsHash == null)
468: createElementsHash();
469:
470: final int numElements = fNumElements;
471:
472: // Check the elements seen for duplicated elements or
473: // elements that aren't permitted, up to insertion point.
474: boolean elementSeen[] = new boolean[numElements];
475:
476: final int numChildren = info.curChildren.length;
477: final int insertAt = info.insertAt;
478: final QName curChildren[] = info.curChildren;
479:
480: for (int childIndex = 0; childIndex < insertAt; childIndex++) {
481: QName currChild = curChildren[childIndex];
482:
483: Integer foundIdx = (Integer) fElementsHash.get(currChild);
484:
485: // If this element was not found in the Hashtable of permitted
486: // elements or was seen already, indicate an error was found
487: // at the current index.
488: if (foundIdx == null)
489: return childIndex;
490:
491: int foundIdxVal = foundIdx.intValue();
492:
493: if (elementSeen[foundIdxVal])
494: return childIndex;
495:
496: elementSeen[foundIdxVal] = true;
497: }
498:
499: info.canHoldPCData = fIsMixed;
500:
501: final int resultsCount = numElements - insertAt;
502: info.resultsCount = resultsCount;
503:
504: //
505: // If the outgoing arrays are too small or null, create new ones. These
506: // have to be at least the size of the results count.
507: //
508: if ((info.results == null)
509: || (info.results.length < resultsCount))
510: info.results = new boolean[resultsCount];
511:
512: if ((info.possibleChildren == null)
513: || (info.possibleChildren.length < resultsCount)) {
514: info.possibleChildren = new QName[resultsCount];
515:
516: QName possibleChildren[] = info.possibleChildren;
517: final int possibleChildrenLen = info.possibleChildren.length;
518:
519: for (int i = 0; i < possibleChildrenLen; i++) {
520: possibleChildren[i] = new QName();
521: }
522: }
523:
524: int possibleChildIdx = 0;
525:
526: // Copy children that haven't been seen yet - they are
527: // the elements that may appear at the insertion point.
528: for (int elemIdx = 0; elemIdx < numElements; elemIdx++) {
529: if (!elementSeen[elemIdx]) {
530: info.possibleChildren[possibleChildIdx]
531: .setValues(fAllElements[elemIdx]);
532: info.results[possibleChildIdx] = true;
533: possibleChildIdx++;
534: }
535: }
536:
537: // EOC is valid only if all elements in ALL have been seen.
538: info.isValidEOC = (resultsCount == 0);
539:
540: if (DEBUG_VALIDATE_CONTENT)
541: System.out.println("Leaving AllContentModel#whatCanGoHere");
542:
543: if (resultsCount == 0)
544: return -1;
545:
546: return info.childCount;
547: // ????: How do we deal with "fullyValidCheck"????
548: }
549:
550: public ContentLeafNameTypeVector getContentLeafNameTypeVector() {
551: if (DEBUG_VALIDATE_CONTENT)
552: System.out
553: .println("Entering AllContentModel#getContentLeafNameTypeVector");
554:
555: if (DEBUG_VALIDATE_CONTENT)
556: System.out
557: .println("Leaving AllContentModel#getContentLeafNameTypeVector");
558: // ???? HZ: What do I need to do here? ????
559: return null;
560: }
561:
562: private void createElementsHash() {
563: int numElements = fNumElements;
564: fElementsHash = new Hashtable(numElements);
565:
566: for (int elementIdx = 0; elementIdx < numElements; elementIdx++) {
567: // Won't do anything to handle duplicates here. That
568: // is left to Unique Particle Constraint checking.
569: fElementsHash.put(fAllElements[elementIdx], new Integer(
570: elementIdx));
571: }
572: }
573:
574: private void showAllElements() {
575: for (int elementIdx = 0; elementIdx < fNumElements; elementIdx++) {
576: System.out.print("fAllElements[" + elementIdx + "] == "
577: + fAllElements[elementIdx].toString());
578:
579: if (fIsOptionalElement[elementIdx]) {
580: System.out.print(" (optional)");
581: }
582:
583: System.out.println();
584: }
585: }
586: }
|