KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > instruct > Attribute


1 package net.sf.saxon.instruct;
2 import net.sf.saxon.Controller;
3 import net.sf.saxon.Err;
4 import net.sf.saxon.Configuration;
5 import net.sf.saxon.event.ReceiverOptions;
6 import net.sf.saxon.event.SequenceReceiver;
7 import net.sf.saxon.expr.*;
8 import net.sf.saxon.om.*;
9 import net.sf.saxon.pattern.NodeKindTest;
10 import net.sf.saxon.style.StandardNames;
11 import net.sf.saxon.trans.DynamicError;
12 import net.sf.saxon.trans.StaticError;
13 import net.sf.saxon.trans.XPathException;
14 import net.sf.saxon.type.*;
15 import net.sf.saxon.value.QNameValue;
16 import net.sf.saxon.value.SequenceType;
17 import net.sf.saxon.value.StringValue;
18
19 import java.io.PrintStream JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Iterator JavaDoc;
22
23 /**
24 * An instruction derived from an xsl:attribute element in stylesheet, or from
25  * an attribute constructor in XQuery
26 */

27
28 public final class Attribute extends SimpleNodeConstructor {
29
30     private Expression attributeName;
31     private Expression namespace=null;
32     private NamespaceResolver nsContext;
33     private SimpleType schemaType;
34     private int annotation;
35     private int validationAction;
36     private boolean allowNameAsQName;
37     private int options;
38
39     /**
40     * Construct an Attribute instruction
41     * @param attributeName An expression to calculate the attribute name
42      * @param namespace An expression to calculate the attribute namespace
43      * @param nsContext a NamespaceContext object containing the static namespace context of the
44     * stylesheet instruction
45      * @param validationAction e.g. validation=strict, lax, strip, preserve
46      * @param schemaType Type against which the attribute must be validated. This must not be a namespace-sensitive
47      * type; it is the caller's responsibility to check this.
48      * @param annotation Integer code identifying the type named in the <code>type</code> attribute
49      * @param allowNameAsQName
50      */

51
52     public Attribute (Expression attributeName,
53                       Expression namespace,
54                       NamespaceResolver nsContext,
55                       int validationAction,
56                       SimpleType schemaType,
57                       int annotation,
58                       boolean allowNameAsQName) {
59         this.attributeName = attributeName;
60         this.namespace = namespace;
61         this.nsContext = nsContext;
62         this.schemaType = schemaType;
63         if (annotation == -1) {
64             this.annotation = StandardNames.XDT_UNTYPED_ATOMIC;
65         } else {
66             this.annotation = annotation;
67         }
68         this.validationAction = validationAction;
69         this.options = 0;
70         this.allowNameAsQName = allowNameAsQName;
71         adoptChildExpression(attributeName);
72         adoptChildExpression(namespace);
73     }
74
75     /**
76      * Indicate that two attributes with the same name are not acceptable.
77      * (This option is set in XQuery, but not in XSLT)
78      */

79
80     public void setRejectDuplicates() {
81         this.options |= ReceiverOptions.REJECT_DUPLICATES;
82     }
83
84     /**
85     * Get the name of this instruction
86     */

87
88     public int getInstructionNameCode() {
89         return StandardNames.XSL_ATTRIBUTE;
90     }
91
92     public ItemType getItemType() {
93         return NodeKindTest.ATTRIBUTE;
94     }
95
96     public int getCardinality() {
97         return StaticProperty.EXACTLY_ONE;
98     }
99
100     /**
101      * Get the static properties of this expression (other than its type). The result is
102      * bit-signficant. These properties are used for optimizations. In general, if
103      * property bit is set, it is true, but if it is unset, the value is unknown.
104      *
105      * @return a set of flags indicating static properties of this expression
106      */

107
108     public int computeSpecialProperties() {
109         return super.computeSpecialProperties() |
110                 StaticProperty.SINGLE_DOCUMENT_NODESET;
111     }
112
113
114      public Expression simplify(StaticContext env) throws XPathException {
115         attributeName = attributeName.simplify(env);
116         if (namespace!=null) {
117             namespace = namespace.simplify(env);
118         }
119         return super.simplify(env);
120     }
121
122     public void localTypeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
123         attributeName = attributeName.typeCheck(env, contextItemType);
124         adoptChildExpression(attributeName);
125
126         RoleLocator role = new RoleLocator(RoleLocator.INSTRUCTION, "attribute/name", 0, null);
127         role.setSourceLocator(this);
128
129         if (allowNameAsQName) {
130             // Can only happen in XQuery
131
attributeName = TypeChecker.staticTypeCheck(attributeName,
132                     SequenceType.SINGLE_ATOMIC, false, role, env);
133         } else {
134             attributeName = TypeChecker.staticTypeCheck(attributeName,
135                     SequenceType.SINGLE_STRING, false, role, env);
136         }
137
138         if (namespace != null) {
139             namespace.typeCheck(env, contextItemType);
140             adoptChildExpression(namespace);
141
142             role = new RoleLocator(RoleLocator.INSTRUCTION, "attribute/namespace", 0, null);
143             role.setSourceLocator(this);
144             namespace = TypeChecker.staticTypeCheck(
145                     namespace, SequenceType.SINGLE_STRING, false, role, env);
146         }
147     }
148
149     /**
150      * Get the subexpressions of this expression
151      * @return an iterator over the subexpressions
152      */

153
154     public Iterator JavaDoc iterateSubExpressions() {
155         ArrayList JavaDoc list = new ArrayList JavaDoc(10);
156         if (select != null) {
157             list.add(select);
158         }
159         list.add(attributeName);
160         if (namespace != null) {
161             list.add(namespace);
162         }
163         return list.iterator();
164     }
165
166    /**
167      * Offer promotion for subexpressions. The offer will be accepted if the subexpression
168      * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
169      * By default the offer is not accepted - this is appropriate in the case of simple expressions
170      * such as constant values and variable references where promotion would give no performance
171      * advantage. This method is always called at compile time.
172      *
173      * @param offer details of the offer, for example the offer to move
174      * expressions that don't depend on the context to an outer level in
175      * the containing expression
176      * @exception XPathException if any error is detected
177      */

178
179     protected void promoteInst(PromotionOffer offer) throws XPathException {
180         attributeName = doPromotion(attributeName, offer);
181         if (namespace != null) {
182             namespace = doPromotion(namespace, offer);
183         }
184         super.promoteInst(offer);
185     }
186
187     /**
188      * Check that any elements and attributes constructed or returned by this expression are acceptable
189      * in the content model of a given complex type. It's always OK to say yes, since the check will be
190      * repeated at run-time. The process of checking element and attribute constructors against the content
191      * model of a complex type also registers the type of content expected of those constructors, so the
192      * static validation can continue recursively.
193      */

194
195     public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
196         if (parentType instanceof SimpleType) {
197             StaticError err = new StaticError(
198                     "Attributes are not permitted here: the containing element is of simple type " +
199                     parentType.getDescription());
200             err.setIsTypeError(true);
201             err.setLocator(this);
202             throw err;
203         }
204     }
205
206     /**
207     * Process this instruction
208     * @param context the dynamic context of the transformation
209     * @return a TailCall to be executed by the caller, always null for this instruction
210     */

211
212     public TailCall processLeavingTail(XPathContext context) throws XPathException
213     {
214         Controller controller = context.getController();
215         int nameCode = evaluateNameCode(context);
216         if (nameCode == -1) {
217             return null;
218         }
219         SequenceReceiver out = context.getReceiver();
220         int opt = options;
221         int ann = annotation;
222
223         // we may need to change the namespace prefix if the one we chose is
224
// already in use with a different namespace URI: this is done behind the scenes
225
// by the Outputter
226

227         String JavaDoc value = expandChildren(context).toString();
228         if (schemaType != null) {
229             // test whether the value actually conforms to the given type
230
try {
231                 XPathException err = schemaType.validateContent(value, DummyNamespaceResolver.getInstance(), context);
232                 if (err != null) {
233                     throw new ValidationException("Attribute value " + Err.wrap(value, Err.VALUE) +
234                                                " does not match the required type " +
235                                                schemaType.getDescription() + ". " +
236                                                err.getMessage());
237                 }
238             } catch (UnresolvedReferenceException ure) {
239                 throw new ValidationException(ure);
240             }
241         } else if (validationAction==Validation.STRICT ||
242                 validationAction==Validation.LAX) {
243             try {
244                 ann = controller.getConfiguration().validateAttribute(
245                         nameCode, value, validationAction);
246             } catch (ValidationException e) {
247                 DynamicError err = DynamicError.makeDynamicError(e);
248                 err.setErrorCode(e.getErrorCodeLocalPart());
249                 err.setXPathContext(context);
250                 err.setLocator(this);
251                 err.setIsTypeError(true);
252                 throw err;
253             }
254         }
255         try {
256             out.attribute(nameCode, ann, value, locationId, opt);
257         } catch (XPathException err) {
258             throw dynamicError(this, err, context);
259         }
260
261         return null;
262     }
263
264     protected int evaluateNameCode(XPathContext context) throws XPathException, XPathException {
265         Controller controller = context.getController();
266         NamePool pool = controller.getNamePool();
267
268         Item nameValue = attributeName.evaluateItem(context);
269
270         String JavaDoc prefix = null;
271         String JavaDoc localName = null;
272
273         if (nameValue instanceof StringValue) {
274             // this will always be the case in XSLT
275
CharSequence JavaDoc rawName = nameValue.getStringValueCS();
276             try {
277                 String JavaDoc[] parts = Name.getQNameParts(rawName);
278                 prefix = parts[0];
279                 localName = parts[1];
280             } catch (QNameException err) {
281                 DynamicError err1 = new DynamicError("Invalid attribute name: " + rawName, this);
282                 err1.setErrorCode("XTDE0850");
283                 err1.setXPathContext(context);
284                 throw dynamicError(this, err1, context);
285             }
286             if (rawName.equals("xmlns")) {
287                 if (namespace==null) {
288                     DynamicError err = new DynamicError("Invalid attribute name: " + rawName, this);
289                     if (context.getController().getExecutable().getHostLanguage() == Configuration.XQUERY) {
290                         err.setErrorCode("XQDY0044");
291                     } else {
292                         err.setErrorCode("XTDE0855");
293                     }
294                     err.setXPathContext(context);
295                     throw dynamicError(this, err, context);
296                 }
297             }
298             if (prefix.equals("xmlns")) {
299                 if (namespace==null) {
300                     DynamicError err = new DynamicError("Invalid attribute name: " + rawName, this);
301                     if (context.getController().getExecutable().getHostLanguage() == Configuration.XQUERY) {
302                         err.setErrorCode("XQDY0044");
303                     } else {
304                         err.setErrorCode("XTDE0860");
305                     }
306                     err.setXPathContext(context);
307                     throw dynamicError(this, err, context);
308                 } else {
309                     // ignore the prefix "xmlns"
310
prefix = "";
311                 }
312             }
313
314         } else if (nameValue instanceof QNameValue) {
315             // this is allowed in XQuery
316
localName = ((QNameValue)nameValue).getLocalName();
317             String JavaDoc namespaceURI = ((QNameValue)nameValue).getNamespaceURI();
318             if (namespaceURI == null) {
319                 namespaceURI = "";
320             }
321             namespace = new StringValue(namespaceURI);
322             if (namespaceURI.equals("")) {
323                 prefix = "";
324             } else {
325                 prefix = ((QNameValue)nameValue).getPrefix();
326                 // If the prefix is a duplicate, a different one will be substituted
327
}
328
329         }
330
331         String JavaDoc uri;
332
333         if (namespace==null) {
334             if ("".equals(prefix)) {
335                 uri = "";
336             } else {
337                 uri = nsContext.getURIForPrefix(prefix, false);
338                 if (uri==null) {
339                     DynamicError err = new DynamicError("Undeclared prefix in attribute name: " + prefix, this);
340                     err.setErrorCode("XTDE0860");
341                     err.setXPathContext(context);
342                     throw dynamicError(this, err, context);
343                 }
344             }
345
346         } else {
347
348             // generate a name using the supplied namespace URI
349

350             uri = namespace.evaluateAsString(context);
351             if ("".equals(uri)) {
352                 // there is a special rule for this case in the specification;
353
// we force the attribute to go in the null namespace
354
prefix = "";
355
356             } else {
357                 // if a suggested prefix is given, use it; otherwise try to find a prefix
358
// associated with this URI; if all else fails, invent one.
359
if ("".equals(prefix)) {
360                     prefix = pool.suggestPrefixForURI(uri);
361                     if (prefix == null) {
362                         prefix = "ns0";
363                         // this will be replaced later if it is already in use
364
}
365                 }
366             }
367         }
368
369         return pool.allocate(prefix, uri, localName);
370     }
371
372     /**
373      * Display this instruction as an expression, for diagnostics
374      */

375
376     public void display(int level, NamePool pool, PrintStream JavaDoc out) {
377         out.println(ExpressionTool.indent(level) + "attribute ");
378         out.println(ExpressionTool.indent(level+1) + "name");
379         attributeName.display(level+2, pool, out);
380         super.display(level+1, pool, out);
381     }
382 }
383
384 //
385
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
386
// you may not use this file except in compliance with the License. You may obtain a copy of the
387
// License at http://www.mozilla.org/MPL/
388
//
389
// Software distributed under the License is distributed on an "AS IS" basis,
390
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
391
// See the License for the specific language governing rights and limitations under the License.
392
//
393
// The Original Code is: all this file.
394
//
395
// The Initial Developer of the Original Code is Michael H. Kay.
396
//
397
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
398
//
399
// Contributor(s): none.
400
//
401
Popular Tags