1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xerces.internal.impl.xs.identity;
23
24import com.sun.org.apache.xerces.internal.impl.xpath.XPathException;
25import com.sun.org.apache.xerces.internal.impl.xs.util.ShortListImpl;
26import com.sun.org.apache.xerces.internal.util.SymbolTable;
27import com.sun.org.apache.xerces.internal.util.XMLChar;
28import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
29import com.sun.org.apache.xerces.internal.xs.ShortList;
30import com.sun.org.apache.xerces.internal.xs.XSComplexTypeDefinition;
31import com.sun.org.apache.xerces.internal.xs.XSConstants;
32import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
33
34/**
35 * Schema identity constraint field.
36 *
37 * @xerces.internal
38 *
39 * @author Andy Clark, IBM
40 */
41public class Field {
42
43    //
44    // Data
45    //
46
47    /** Field XPath. */
48    protected final Field.XPath fXPath;
49
50
51    /** Identity constraint. */
52    protected final IdentityConstraint fIdentityConstraint;
53
54    //
55    // Constructors
56    //
57
58    /** Constructs a field. */
59    public Field(Field.XPath xpath,
60                 IdentityConstraint identityConstraint) {
61        fXPath = xpath;
62        fIdentityConstraint = identityConstraint;
63    } // <init>(Field.XPath,IdentityConstraint)
64
65    //
66    // Public methods
67    //
68
69    /** Returns the field XPath. */
70    public com.sun.org.apache.xerces.internal.impl.xpath.XPath getXPath() {
71        return fXPath;
72    } // getXPath():org.apache.xerces.impl.v1.schema.identity.XPath
73
74    /** Returns the identity constraint. */
75    public IdentityConstraint getIdentityConstraint() {
76        return fIdentityConstraint;
77    } // getIdentityConstraint():IdentityConstraint
78
79    // factory method
80
81    /** Creates a field matcher. */
82    public XPathMatcher createMatcher(ValueStore store) {
83        return new Field.Matcher(fXPath, store);
84    } // createMatcher(ValueStore):XPathMatcher
85
86    //
87    // Object methods
88    //
89
90    /** Returns a string representation of this object. */
91    public String toString() {
92        return fXPath.toString();
93    } // toString():String
94
95    //
96    // Classes
97    //
98
99    /**
100     * Field XPath.
101     *
102     * @author Andy Clark, IBM
103     */
104    public static class XPath
105        extends com.sun.org.apache.xerces.internal.impl.xpath.XPath {
106
107        //
108        // Constructors
109        //
110
111        /** Constructs a field XPath expression. */
112        public XPath(String xpath,
113                     SymbolTable symbolTable,
114                     NamespaceContext context) throws XPathException {
115            super(fixupXPath(xpath), symbolTable, context);
116
117            // verify that only one attribute is selected per branch
118            for (int i=0;i<fLocationPaths.length;i++) {
119                for(int j=0; j<fLocationPaths[i].steps.length; j++) {
120                    com.sun.org.apache.xerces.internal.impl.xpath.XPath.Axis axis =
121                        fLocationPaths[i].steps[j].axis;
122                    if (axis.type == XPath.Axis.ATTRIBUTE &&
123                            (j < fLocationPaths[i].steps.length-1)) {
124                        throw new XPathException("c-fields-xpaths");
125                    }
126                }
127            }
128        } // <init>(String,SymbolTable,NamespacesContext)
129
130        /** Fixup XPath expression. Avoid creating a new String if possible. */
131        private static String fixupXPath(String xpath) {
132
133            final int end = xpath.length();
134            int offset = 0;
135            boolean whitespace = true;
136            char c;
137
138            // NOTE: We have to prefix the field XPath with "./" in
139            //       order to handle selectors such as "@attr" that
140            //       select the attribute because the fields could be
141            //       relative to the selector element. -Ac
142            //       Unless xpath starts with a descendant node -Achille Fokoue
143            //      ... or a / or a . - NG
144            for (; offset < end; ++offset) {
145                c = xpath.charAt(offset);
146                if (whitespace) {
147                    if (!XMLChar.isSpace(c)) {
148                        if (c == '.' || c == '/') {
149                            whitespace = false;
150                        }
151                        else if (c != '|') {
152                            return fixupXPath2(xpath, offset, end);
153                        }
154                    }
155                }
156                else if (c == '|') {
157                    whitespace = true;
158                }
159            }
160            return xpath;
161
162        } // fixupXPath(String):String
163
164        private static String fixupXPath2(String xpath, int offset, final int end) {
165
166            StringBuffer buffer = new StringBuffer(end + 2);
167            for (int i = 0; i < offset; ++i) {
168                buffer.append(xpath.charAt(i));
169            }
170            buffer.append("./");
171
172            boolean whitespace = false;
173            char c;
174
175            for (; offset < end; ++offset) {
176                c = xpath.charAt(offset);
177                if (whitespace) {
178                    if (!XMLChar.isSpace(c)) {
179                        if (c == '.' || c == '/') {
180                            whitespace = false;
181                        }
182                        else if (c != '|') {
183                            buffer.append("./");
184                            whitespace = false;
185                        }
186                    }
187                }
188                else if (c == '|') {
189                    whitespace = true;
190                }
191                buffer.append(c);
192            }
193            return buffer.toString();
194
195        } // fixupXPath2(String, int, int):String
196
197    } // class XPath
198
199    /**
200     * Field matcher.
201     *
202     * @author Andy Clark, IBM
203     */
204    protected class Matcher
205        extends XPathMatcher {
206
207        //
208        // Data
209        //
210
211        /** Value store for data values. */
212        protected final ValueStore fStore;
213
214        /** A flag indicating whether the field is allowed to match a value. */
215        protected boolean fMayMatch = true;
216
217        //
218        // Constructors
219        //
220
221        /** Constructs a field matcher. */
222        public Matcher(Field.XPath xpath, ValueStore store) {
223            super(xpath);
224            fStore = store;
225        } // <init>(Field.XPath,ValueStore)
226
227        //
228        // XPathHandler methods
229        //
230
231        /**
232         * This method is called when the XPath handler matches the
233         * XPath expression.
234         */
235        protected void matched(Object actualValue, short valueType, ShortList itemValueType, boolean isNil) {
236            super.matched(actualValue, valueType, itemValueType, isNil);
237            if(isNil && (fIdentityConstraint.getCategory() == IdentityConstraint.IC_KEY)) {
238                String code = "KeyMatchesNillable";
239                fStore.reportError(code,
240                    new Object[]{fIdentityConstraint.getElementName(), fIdentityConstraint.getIdentityConstraintName()});
241            }
242            fStore.addValue(Field.this, fMayMatch, actualValue, convertToPrimitiveKind(valueType), convertToPrimitiveKind(itemValueType));
243            // once we've stored the value for this field, we set the mayMatch
244            // member to false so that in the same scope, we don't match any more
245            // values (and throw an error instead).
246            fMayMatch = false;
247        } // matched(String)
248
249        private short convertToPrimitiveKind(short valueType) {
250            /** Primitive datatypes. */
251            if (valueType <= XSConstants.NOTATION_DT) {
252                return valueType;
253            }
254            /** Types derived from string. */
255            if (valueType <= XSConstants.ENTITY_DT) {
256                return XSConstants.STRING_DT;
257            }
258            /** Types derived from decimal. */
259            if (valueType <= XSConstants.POSITIVEINTEGER_DT) {
260                return XSConstants.DECIMAL_DT;
261            }
262            /** Other types. */
263            return valueType;
264        }
265
266        private ShortList convertToPrimitiveKind(ShortList itemValueType) {
267            if (itemValueType != null) {
268                int i;
269                final int length = itemValueType.getLength();
270                for (i = 0; i < length; ++i) {
271                    short type = itemValueType.item(i);
272                    if (type != convertToPrimitiveKind(type)) {
273                        break;
274                    }
275                }
276                if (i != length) {
277                    final short [] arr = new short[length];
278                    for (int j = 0; j < i; ++j) {
279                        arr[j] = itemValueType.item(j);
280                    }
281                    for(; i < length; ++i) {
282                        arr[i] = convertToPrimitiveKind(itemValueType.item(i));
283                    }
284                    return new ShortListImpl(arr, arr.length);
285                }
286            }
287            return itemValueType;
288        }
289
290        protected void handleContent(XSTypeDefinition type, boolean nillable, Object actualValue, short valueType, ShortList itemValueType) {
291            if (type == null ||
292               type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE &&
293               ((XSComplexTypeDefinition) type).getContentType()
294                != 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
309