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.xs.identity;
019:
020: import org.apache.xerces.impl.xpath.XPathException;
021: import org.apache.xerces.impl.xs.util.ShortListImpl;
022: import org.apache.xerces.util.SymbolTable;
023: import org.apache.xerces.util.XMLChar;
024: import org.apache.xerces.xni.NamespaceContext;
025: import org.apache.xerces.xs.ShortList;
026: import org.apache.xerces.xs.XSComplexTypeDefinition;
027: import org.apache.xerces.xs.XSConstants;
028: import org.apache.xerces.xs.XSTypeDefinition;
029:
030: /**
031: * Schema identity constraint field.
032: *
033: * @xerces.internal
034: *
035: * @author Andy Clark, IBM
036: * @version $Id: Field.java 572110 2007-09-02 19:04:44Z mrglavas $
037: */
038: public class Field {
039:
040: //
041: // Data
042: //
043:
044: /** Field XPath. */
045: protected final Field.XPath fXPath;
046:
047: /** Identity constraint. */
048: protected final IdentityConstraint fIdentityConstraint;
049:
050: //
051: // Constructors
052: //
053:
054: /** Constructs a field. */
055: public Field(Field.XPath xpath,
056: IdentityConstraint identityConstraint) {
057: fXPath = xpath;
058: fIdentityConstraint = identityConstraint;
059: } // <init>(Field.XPath,IdentityConstraint)
060:
061: //
062: // Public methods
063: //
064:
065: /** Returns the field XPath. */
066: public org.apache.xerces.impl.xpath.XPath getXPath() {
067: return fXPath;
068: } // getXPath():org.apache.xerces.impl.v1.schema.identity.XPath
069:
070: /** Returns the identity constraint. */
071: public IdentityConstraint getIdentityConstraint() {
072: return fIdentityConstraint;
073: } // getIdentityConstraint():IdentityConstraint
074:
075: // factory method
076:
077: /** Creates a field matcher. */
078: public XPathMatcher createMatcher(ValueStore store) {
079: return new Field.Matcher(fXPath, store);
080: } // createMatcher(ValueStore):XPathMatcher
081:
082: //
083: // Object methods
084: //
085:
086: /** Returns a string representation of this object. */
087: public String toString() {
088: return fXPath.toString();
089: } // toString():String
090:
091: //
092: // Classes
093: //
094:
095: /**
096: * Field XPath.
097: *
098: * @author Andy Clark, IBM
099: */
100: public static class XPath extends
101: org.apache.xerces.impl.xpath.XPath {
102:
103: //
104: // Constructors
105: //
106:
107: /** Constructs a field XPath expression. */
108: public XPath(String xpath, SymbolTable symbolTable,
109: NamespaceContext context) throws XPathException {
110: super (fixupXPath(xpath), symbolTable, context);
111:
112: // verify that only one attribute is selected per branch
113: for (int i = 0; i < fLocationPaths.length; i++) {
114: for (int j = 0; j < fLocationPaths[i].steps.length; j++) {
115: org.apache.xerces.impl.xpath.XPath.Axis axis = fLocationPaths[i].steps[j].axis;
116: if (axis.type == XPath.Axis.ATTRIBUTE
117: && (j < fLocationPaths[i].steps.length - 1)) {
118: throw new XPathException("c-fields-xpaths");
119: }
120: }
121: }
122: } // <init>(String,SymbolTable,NamespacesContext)
123:
124: /** Fixup XPath expression. Avoid creating a new String if possible. */
125: private static String fixupXPath(String xpath) {
126:
127: final int end = xpath.length();
128: int offset = 0;
129: boolean whitespace = true;
130: char c;
131:
132: // NOTE: We have to prefix the field XPath with "./" in
133: // order to handle selectors such as "@attr" that
134: // select the attribute because the fields could be
135: // relative to the selector element. -Ac
136: // Unless xpath starts with a descendant node -Achille Fokoue
137: // ... or a / or a . - NG
138: for (; offset < end; ++offset) {
139: c = xpath.charAt(offset);
140: if (whitespace) {
141: if (!XMLChar.isSpace(c)) {
142: if (c == '.' || c == '/') {
143: whitespace = false;
144: } else if (c != '|') {
145: return fixupXPath2(xpath, offset, end);
146: }
147: }
148: } else if (c == '|') {
149: whitespace = true;
150: }
151: }
152: return xpath;
153:
154: } // fixupXPath(String):String
155:
156: private static String fixupXPath2(String xpath, int offset,
157: final int end) {
158:
159: StringBuffer buffer = new StringBuffer(end + 2);
160: for (int i = 0; i < offset; ++i) {
161: buffer.append(xpath.charAt(i));
162: }
163: buffer.append("./");
164:
165: boolean whitespace = false;
166: char c;
167:
168: for (; offset < end; ++offset) {
169: c = xpath.charAt(offset);
170: if (whitespace) {
171: if (!XMLChar.isSpace(c)) {
172: if (c == '.' || c == '/') {
173: whitespace = false;
174: } else if (c != '|') {
175: buffer.append("./");
176: whitespace = false;
177: }
178: }
179: } else if (c == '|') {
180: whitespace = true;
181: }
182: buffer.append(c);
183: }
184: return buffer.toString();
185:
186: } // fixupXPath2(String, int, int):String
187:
188: } // class XPath
189:
190: /**
191: * Field matcher.
192: *
193: * @author Andy Clark, IBM
194: */
195: protected class Matcher extends XPathMatcher {
196:
197: //
198: // Data
199: //
200:
201: /** Value store for data values. */
202: protected final ValueStore fStore;
203:
204: /** A flag indicating whether the field is allowed to match a value. */
205: protected boolean fMayMatch = true;
206:
207: //
208: // Constructors
209: //
210:
211: /** Constructs a field matcher. */
212: public Matcher(Field.XPath xpath, ValueStore store) {
213: super (xpath);
214: fStore = store;
215: } // <init>(Field.XPath,ValueStore)
216:
217: //
218: // XPathHandler methods
219: //
220:
221: /**
222: * This method is called when the XPath handler matches the
223: * XPath expression.
224: */
225: protected void matched(Object actualValue, short valueType,
226: ShortList itemValueType, boolean isNil) {
227: super .matched(actualValue, valueType, itemValueType, isNil);
228: if (isNil
229: && (fIdentityConstraint.getCategory() == IdentityConstraint.IC_KEY)) {
230: String code = "KeyMatchesNillable";
231: fStore.reportError(code,
232: new Object[] {
233: fIdentityConstraint.getElementName(),
234: fIdentityConstraint
235: .getIdentityConstraintName() });
236: }
237: fStore.addValue(Field.this , fMayMatch, actualValue,
238: convertToPrimitiveKind(valueType),
239: convertToPrimitiveKind(itemValueType));
240: // once we've stored the value for this field, we set the mayMatch
241: // member to false so that in the same scope, we don't match any more
242: // values (and throw an error instead).
243: fMayMatch = false;
244: } // matched(String)
245:
246: private short convertToPrimitiveKind(short valueType) {
247: /** Primitive datatypes. */
248: if (valueType <= XSConstants.NOTATION_DT) {
249: return valueType;
250: }
251: /** Types derived from string. */
252: if (valueType <= XSConstants.ENTITY_DT) {
253: return XSConstants.STRING_DT;
254: }
255: /** Types derived from decimal. */
256: if (valueType <= XSConstants.POSITIVEINTEGER_DT) {
257: return XSConstants.DECIMAL_DT;
258: }
259: /** Other types. */
260: return valueType;
261: }
262:
263: private ShortList convertToPrimitiveKind(ShortList itemValueType) {
264: if (itemValueType != null) {
265: int i;
266: final int length = itemValueType.getLength();
267: for (i = 0; i < length; ++i) {
268: short type = itemValueType.item(i);
269: if (type != convertToPrimitiveKind(type)) {
270: break;
271: }
272: }
273: if (i != length) {
274: final short[] arr = new short[length];
275: for (int j = 0; j < i; ++j) {
276: arr[j] = itemValueType.item(j);
277: }
278: for (; i < length; ++i) {
279: arr[i] = convertToPrimitiveKind(itemValueType
280: .item(i));
281: }
282: return new ShortListImpl(arr, arr.length);
283: }
284: }
285: return itemValueType;
286: }
287:
288: protected void handleContent(XSTypeDefinition type,
289: boolean nillable, Object actualValue, short valueType,
290: ShortList itemValueType) {
291: if (type == null
292: || type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE
293: && ((XSComplexTypeDefinition) type)
294: .getContentType() != XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) {
295:
296: // the content must be simpleType content
297: fStore.reportError("cvc-id.3", new Object[] {
298: fIdentityConstraint.getName(),
299: fIdentityConstraint.getElementName() });
300:
301: }
302: fMatchedString = actualValue;
303: matched(fMatchedString, valueType, itemValueType, nillable);
304: } // handleContent(XSElementDecl, String)
305:
306: } // class Matcher
307:
308: } // class Field
|