001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl.dtd.models;
019:
020: import org.apache.xerces.xni.QName;
021:
022: import org.apache.xerces.impl.dtd.XMLContentSpec;
023:
024: /**
025: * MixedContentModel is a derivative of the abstract content model base
026: * class that handles the special case of mixed model elements. If an element
027: * is mixed model, it has PCDATA as its first possible content, followed
028: * by an alternation of the possible children. The children cannot have any
029: * numeration or order, so it must look like this:
030: * <pre>
031: * <!ELEMENT Foo ((#PCDATA|a|b|c|)*)>
032: * </pre>
033: * So, all we have to do is to keep an array of the possible children and
034: * validate by just looking up each child being validated by looking it up
035: * in the list.
036: *
037: * @xerces.internal
038: *
039: * @version $Id: MixedContentModel.java 572057 2007-09-02 18:03:20Z mrglavas $
040: */
041: public class MixedContentModel implements ContentModelValidator {
042:
043: //
044: // Data
045: //
046:
047: /** The count of possible children that we have to deal with. */
048: private final int fCount;
049:
050: /** The list of possible children that we have to accept. */
051: private final QName fChildren[];
052:
053: /** The type of the children to support ANY. */
054: private final int fChildrenType[];
055:
056: /* this is the EquivClassComparator object */
057: //private EquivClassComparator comparator = null;
058: /**
059: * True if mixed content model is ordered. DTD mixed content models
060: * are <em>always</em> unordered.
061: */
062: private final boolean fOrdered;
063:
064: //
065: // Constructors
066: //
067:
068: /**
069: * Constructs a mixed content model.
070: *
071: * @param children The list of allowed children.
072: * @param type The list of the types of the children.
073: * @param offset The start offset position in the children.
074: * @param length The child count.
075: * @param ordered True if content must be ordered.
076: */
077: public MixedContentModel(QName[] children, int[] type, int offset,
078: int length, boolean ordered) {
079: // Make our own copy now, which is exactly the right size
080: fCount = length;
081: fChildren = new QName[fCount];
082: fChildrenType = new int[fCount];
083: for (int i = 0; i < fCount; i++) {
084: fChildren[i] = new QName(children[offset + i]);
085: fChildrenType[i] = type[offset + i];
086: }
087: fOrdered = ordered;
088:
089: }
090:
091: //
092: // ContentModelValidator methods
093: //
094:
095: /**
096: * Check that the specified content is valid according to this
097: * content model. This method can also be called to do 'what if'
098: * testing of content models just to see if they would be valid.
099: * <p>
100: * A value of -1 in the children array indicates a PCDATA node. All other
101: * indexes will be positive and represent child elements. The count can be
102: * zero, since some elements have the EMPTY content model and that must be
103: * confirmed.
104: *
105: * @param children The children of this element. Each integer is an index within
106: * the <code>StringPool</code> of the child element name. An index
107: * of -1 is used to indicate an occurrence of non-whitespace character
108: * data.
109: * @param offset Offset into the array where the children starts.
110: * @param length The number of entries in the <code>children</code> array.
111: *
112: * @return The value -1 if fully valid, else the 0 based index of the child
113: * that first failed. If the value returned is equal to the number
114: * of children, then the specified children are valid but additional
115: * content is required to reach a valid ending state.
116: *
117: */
118: public int validate(QName[] children, int offset, int length) {
119:
120: // must match order
121: if (fOrdered) {
122: int inIndex = 0;
123: for (int outIndex = 0; outIndex < length; outIndex++) {
124:
125: // ignore mixed text
126: final QName curChild = children[offset + outIndex];
127: if (curChild.localpart == null) {
128: continue;
129: }
130:
131: // element must match
132: int type = fChildrenType[inIndex];
133: if (type == XMLContentSpec.CONTENTSPECNODE_LEAF) {
134: if (fChildren[inIndex].rawname != children[offset
135: + outIndex].rawname) {
136: return outIndex;
137: }
138: } else if (type == XMLContentSpec.CONTENTSPECNODE_ANY) {
139: String uri = fChildren[inIndex].uri;
140: if (uri != null && uri != children[outIndex].uri) {
141: return outIndex;
142: }
143: } else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL) {
144: if (children[outIndex].uri != null) {
145: return outIndex;
146: }
147: } else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER) {
148: if (fChildren[inIndex].uri == children[outIndex].uri) {
149: return outIndex;
150: }
151: }
152:
153: // advance index
154: inIndex++;
155: }
156: }
157:
158: // can appear in any order
159: else {
160: for (int outIndex = 0; outIndex < length; outIndex++) {
161: // Get the current child out of the source index
162: final QName curChild = children[offset + outIndex];
163:
164: // If its PCDATA, then we just accept that
165: if (curChild.localpart == null)
166: continue;
167:
168: // And try to find it in our list
169: int inIndex = 0;
170: for (; inIndex < fCount; inIndex++) {
171: int type = fChildrenType[inIndex];
172: if (type == XMLContentSpec.CONTENTSPECNODE_LEAF) {
173: if (curChild.rawname == fChildren[inIndex].rawname) {
174: break;
175: }
176: } else if (type == XMLContentSpec.CONTENTSPECNODE_ANY) {
177: String uri = fChildren[inIndex].uri;
178: if (uri == null
179: || uri == children[outIndex].uri) {
180: break;
181: }
182: } else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL) {
183: if (children[outIndex].uri == null) {
184: break;
185: }
186: } else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER) {
187: if (fChildren[inIndex].uri != children[outIndex].uri) {
188: break;
189: }
190: }
191: // REVISIT: What about checking for multiple ANY matches?
192: // The content model ambiguity *could* be checked
193: // by the caller before constructing the mixed
194: // content model.
195: }
196:
197: // We did not find this one, so the validation failed
198: if (inIndex == fCount)
199: return outIndex;
200: }
201: }
202:
203: // Everything seems to be in order, so return success
204: return -1;
205: } // validate
206:
207: } // class MixedContentModel
|