KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.instruct;
2
3 import net.sf.saxon.Configuration;
4 import net.sf.saxon.Controller;
5 import net.sf.saxon.expr.*;
6 import net.sf.saxon.functions.NumberFn;
7 import net.sf.saxon.number.NumberFormatter;
8 import net.sf.saxon.number.Numberer;
9 import net.sf.saxon.number.Numberer_en;
10 import net.sf.saxon.om.*;
11 import net.sf.saxon.pattern.Pattern;
12 import net.sf.saxon.pattern.PatternSponsor;
13 import net.sf.saxon.trans.DynamicError;
14 import net.sf.saxon.trans.StaticError;
15 import net.sf.saxon.trans.XPathException;
16 import net.sf.saxon.type.AtomicType;
17 import net.sf.saxon.type.ItemType;
18 import net.sf.saxon.type.Type;
19 import net.sf.saxon.value.*;
20
21 import java.io.PrintStream JavaDoc;
22 import java.util.*;
23
24 /**
25  * An xsl:number element in the stylesheet. Although this is an XSLT instruction, it is compiled
26  * into an expression, evaluated using xsl:value-of to create the resulting text node.<br>
27  */

28
29 public class NumberInstruction extends ComputedExpression {
30
31     private static final int SINGLE = 0;
32     private static final int MULTI = 1;
33     private static final int ANY = 2;
34     private static final int SIMPLE = 3;
35
36     private int level;
37     private Pattern count = null;
38     private Pattern from = null;
39     private Expression select = null;
40     private Expression value = null;
41     private Expression format = null;
42     private Expression groupSize = null;
43     private Expression groupSeparator = null;
44     private Expression letterValue = null;
45     private Expression ordinal = null;
46     private Expression lang = null;
47     private NumberFormatter formatter = null;
48     private Numberer numberer = null;
49     private HashMap nationalNumberers = null;
50     private boolean hasVariablesInPatterns;
51     private boolean backwardsCompatible;
52
53     private static Numberer defaultNumberer = new Numberer_en();
54
55     public NumberInstruction(Expression select,
56                              int level,
57                              Pattern count,
58                              Pattern from,
59                              Expression value,
60                              Expression format,
61                              Expression groupSize,
62                              Expression groupSeparator,
63                              Expression letterValue,
64                              Expression ordinal,
65                              Expression lang,
66                              NumberFormatter formatter,
67                              Numberer numberer,
68                              boolean hasVariablesInPatterns,
69                              boolean backwardsCompatible) {
70         this.select = select;
71         this.level = level;
72         this.count = count;
73         this.from = from;
74         this.value = value;
75         this.format = format;
76         this.groupSize = groupSize;
77         this.groupSeparator = groupSeparator;
78         this.letterValue = letterValue;
79         this.ordinal = ordinal;
80         this.lang = lang;
81         this.formatter = formatter;
82         this.numberer = numberer;
83         this.hasVariablesInPatterns = hasVariablesInPatterns;
84         this.backwardsCompatible = backwardsCompatible;
85
86         if (this.value != null && !Type.isSubType(this.value.getItemType(), Type.ANY_ATOMIC_TYPE)) {
87             this.value = new Atomizer(this.value, null);
88         }
89
90         Iterator kids = iterateSubExpressions();
91         while (kids.hasNext()) {
92             Expression child = (Expression)kids.next();
93             adoptChildExpression(child);
94         }
95
96
97 // if (select != null) {
98
// adoptChildExpression(select);
99
// }
100
// if (value != null) {
101
// adoptChildExpression(value);
102
// }
103
// if (format != null) {
104
// adoptChildExpression(format);
105
// }
106
// if (groupSize != null) {
107
// adoptChildExpression(groupSize);
108
// }
109
// if (groupSeparator != null) {
110
// adoptChildExpression(groupSeparator);
111
// }
112
// if (letterValue != null) {
113
// adoptChildExpression(letterValue);
114
// }
115
// if (ordinal != null) {
116
// adoptChildExpression(ordinal);
117
// }
118
// if (lang != null) {
119
// adoptChildExpression(lang);
120
// }
121
}
122
123     public Expression simplify(StaticContext env) throws XPathException {
124         if (select != null) {
125             select = select.simplify(env);
126         }
127         if (value != null) {
128             value = value.simplify(env);
129         }
130         if (format != null) {
131             format = format.simplify(env);
132         }
133         if (groupSize != null) {
134             groupSize = groupSize.simplify(env);
135         }
136         if (groupSeparator != null) {
137             groupSeparator = groupSeparator.simplify(env);
138         }
139         if (letterValue != null) {
140             letterValue = letterValue.simplify(env);
141         }
142         if (ordinal != null) {
143             ordinal = ordinal.simplify(env);
144         }
145         if (lang != null) {
146             lang = lang.simplify(env);
147         }
148         if (count != null) {
149             count = count.simplify(env);
150         }
151         if (from != null) {
152             from = from.simplify(env);
153         }
154         return this;
155     }
156
157     /**
158      * Perform static analysis of an expression and its subexpressions.
159      *
160      * <p>This checks statically that the operands of the expression have
161      * the correct type; if necessary it generates code to do run-time type checking or type
162      * conversion. A static type error is reported only if execution cannot possibly succeed, that
163      * is, if a run-time type error is inevitable. The call may return a modified form of the expression.</p>
164      *
165      * <p>This method is called after all references to functions and variables have been resolved
166      * to the declaration of the function or variable. However, the types of such functions and
167      * variables will only be accurately known if they have been explicitly declared.</p>
168      *
169      * @param env the static context of the expression
170      * @exception net.sf.saxon.trans.StaticError if an error is discovered during this phase
171      * (typically a type error)
172      * @return the original expression, rewritten to perform necessary
173      * run-time type checks, and to perform other type-related
174      * optimizations
175      */

176
177     public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
178         if (select != null) {
179             select = select.typeCheck(env, contextItemType);
180         } else {
181             if (value==null) {
182                 // we are numbering the context node
183
if (contextItemType instanceof AtomicType) {
184                     StaticError err = new StaticError("xsl:number requires the context item to be a node, but it is an atomic value");
185                     err.setIsTypeError(true);
186                     err.setErrorCode("XTTE0990");
187                 }
188             }
189         }
190         if (value != null) {
191             value = value.typeCheck(env, contextItemType);
192         }
193         if (format != null) {
194             format = format.typeCheck(env, contextItemType);
195         }
196         if (groupSize != null) {
197             groupSize = groupSize.typeCheck(env, contextItemType);
198         }
199         if (groupSeparator != null) {
200             groupSeparator = groupSeparator.typeCheck(env, contextItemType);
201         }
202         if (letterValue != null) {
203             letterValue = letterValue.typeCheck(env, contextItemType);
204         }
205         if (ordinal != null) {
206             ordinal = ordinal.typeCheck(env, contextItemType);
207         }
208         if (lang != null) {
209             lang = lang.typeCheck(env, contextItemType);
210         }
211         if (count != null) {
212             count = count.analyze(env, contextItemType);
213         }
214         if (from != null) {
215             from = from.analyze(env, contextItemType);
216         }
217         return this;
218     }
219
220     /**
221      * Perform optimisation of an expression and its subexpressions.
222      * <p/>
223      * <p>This method is called after all references to functions and variables have been resolved
224      * to the declaration of the function or variable, and after all type checking has been done.</p>
225      *
226      * @param opt the optimizer in use. This provides access to supporting functions; it also allows
227      * different optimization strategies to be used in different circumstances.
228      * @param env the static context of the expression
229      * @param contextItemType the static type of "." at the point where this expression is invoked.
230      * The parameter is set to null if it is known statically that the context item will be undefined.
231      * If the type of the context item is not known statically, the argument is set to
232      * {@link net.sf.saxon.type.Type#ITEM_TYPE}
233      * @return the original expression, rewritten if appropriate to optimize execution
234      * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
235      * (typically a type error)
236      */

237
238     public Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) throws XPathException {
239         if (select != null) {
240             select = select.optimize(opt, env, contextItemType);
241         }
242         if (value != null) {
243             value = value.optimize(opt, env, contextItemType);
244         }
245         if (format != null) {
246             format = format.optimize(opt, env, contextItemType);
247         }
248         if (groupSize != null) {
249             groupSize = groupSize.optimize(opt, env, contextItemType);
250         }
251         if (groupSeparator != null) {
252             groupSeparator = groupSeparator.optimize(opt, env, contextItemType);
253         }
254         if (letterValue != null) {
255             letterValue = letterValue.optimize(opt, env, contextItemType);
256         }
257         if (ordinal != null) {
258             ordinal = ordinal.optimize(opt, env, contextItemType);
259         }
260         if (lang != null) {
261             lang = lang.optimize(opt, env, contextItemType);
262         }
263 // if (count != null) {
264
// count = count.analyze(env, contextItemType);
265
// }
266
// if (from != null) {
267
// from = from.analyze(env, contextItemType);
268
// }
269
return this;
270     }
271
272
273    /**
274      * Get the immediate sub-expressions of this expression. Default implementation
275      * returns a zero-length array, appropriate for an expression that has no
276      * sub-expressions.
277      * @return an iterator containing the sub-expressions of this expression
278      */

279
280     public Iterator iterateSubExpressions() {
281         List sub = new ArrayList(9);
282         if (select != null) {
283             sub.add(select);
284         }
285         if (value != null) {
286             sub.add(value);
287         }
288         if (format != null) {
289             sub.add(format);
290         }
291         if (groupSize != null) {
292             sub.add(groupSize);
293         }
294         if (groupSeparator != null) {
295             sub.add(groupSeparator);
296         }
297         if (letterValue != null) {
298             sub.add(letterValue);
299         }
300         if (ordinal != null) {
301             sub.add(ordinal);
302         }
303         if (lang != null) {
304             sub.add(lang);
305         }
306         if (count != null) {
307             sub.add(new PatternSponsor(count));
308         }
309         if (from != null) {
310             sub.add(new PatternSponsor(from));
311         }
312         return sub.iterator();
313     }
314
315     /**
316      * Determine the intrinsic dependencies of an expression, that is, those which are not derived
317      * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
318      * on the context position, while (position()+1) does not. The default implementation
319      * of the method returns 0, indicating "no dependencies".
320      *
321      * @return a set of bit-significant flags identifying the "intrinsic"
322      * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
323      */

324
325     public int getIntrinsicDependencies() {
326         return (select == null ? StaticProperty.DEPENDS_ON_CONTEXT_ITEM : 0);
327     }
328
329     public ItemType getItemType() {
330         return Type.STRING_TYPE;
331     }
332
333     public int computeCardinality() {
334         return StaticProperty.EXACTLY_ONE;
335     }
336
337     /**
338      * Offer promotion for this subexpression. The offer will be accepted if the subexpression
339      * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
340      * By default the offer is not accepted - this is appropriate in the case of simple expressions
341      * such as constant values and variable references where promotion would give no performance
342      * advantage. This method is always called at compile time.
343      *
344      * @param offer details of the offer, for example the offer to move
345      * expressions that don't depend on the context to an outer level in
346      * the containing expression
347      * @return if the offer is not accepted, return this expression unchanged.
348      * Otherwise return the result of rewriting the expression to promote
349      * this subexpression
350      * @throws net.sf.saxon.trans.XPathException
351      * if any error is detected
352      */

353
354     public Expression promote(PromotionOffer offer) throws XPathException {
355         Expression exp = offer.accept(this);
356         if (exp!=null) {
357             return exp;
358         } else {
359             if (select != null) {
360                 select = doPromotion(select, offer);
361             }
362             if (value != null) {
363                 value = doPromotion(value, offer);
364             }
365             if (format != null) {
366                 format = doPromotion(format, offer);
367             }
368             if (groupSize != null) {
369                 groupSize = doPromotion(groupSize, offer);
370             }
371             if (groupSeparator != null) {
372                 groupSeparator = doPromotion(groupSeparator, offer);
373             }
374             if (letterValue != null) {
375                 letterValue = doPromotion(letterValue, offer);
376             }
377             if (ordinal != null) {
378                 ordinal = doPromotion(ordinal, offer);
379             }
380             if (lang != null) {
381                 lang = doPromotion(lang, offer);
382             }
383             if (count != null) {
384                 count.promote(offer);
385             }
386             if (from != null) {
387                 from.promote(offer);
388             }
389             return this;
390         }
391     }
392
393     public Item evaluateItem(XPathContext context) throws XPathException {
394         long value = -1;
395         List vec = null; // a list whose items may be of type either Long or
396
// BigInteger or the string to be output (e.g. "NaN")
397

398         if (this.value != null) {
399
400             SequenceIterator iter = this.value.iterate(context);
401             vec = new ArrayList(4);
402             while (true) {
403                 AtomicValue val = (AtomicValue) iter.next();
404                 if (val == null) {
405                     break;
406                 }
407                 try {
408                     NumericValue num;
409                     if (val instanceof NumericValue) {
410                         num = (NumericValue) val;
411                     } else {
412                         num = NumberFn.convert(val);
413                     }
414                     if (num.isNaN()) {
415                         throw new DynamicError("NaN"); // thrown to be caught
416
}
417                     num = num.round();
418                     if (num.compareTo(IntegerValue.MAX_LONG) > 0) {
419                         vec.add(((BigIntegerValue)num.convert(Type.INTEGER, context)).getBigInteger());
420 // DynamicError e = new DynamicError("A number is too large to be formatted");
421
// e.setXPathContext(context);
422
// e.setErrorCode("SAXON:0000");
423
// throw e;
424
} else {
425                         if (num.compareTo(IntegerValue.ZERO) < 0) {
426                             DynamicError e = new DynamicError("The numbers to be formatted must not be negative");
427 // e.setXPathContext(context);
428
// e.setErrorCode("XT0980");
429
throw e;
430                         }
431                         long i = ((NumericValue) num.convert(Type.INTEGER, context)).longValue();
432     // if (i < 0) {
433
// DynamicError e = new DynamicError("The numbers to be formatted must not be negative");
434
// e.setXPathContext(context);
435
// e.setErrorCode("XT0980");
436
// throw e;
437
// }
438
vec.add(new Long JavaDoc(i));
439                     }
440                 } catch (DynamicError err) {
441                     if (backwardsCompatible) {
442                         vec.add("NaN");
443                     } else {
444                         vec.add(val.getStringValue());
445                         DynamicError e = new DynamicError("Cannot convert supplied value to an integer. " + err.getMessage());
446                         e.setErrorCode("XTDE0980");
447                         e.setXPathContext(context);
448                         throw e;
449                     }
450                 }
451             }
452             if (backwardsCompatible && vec.size()==0) {
453                 vec.add("NaN");
454             }
455         } else {
456             NodeInfo source;
457             if (select != null) {
458                 source = (NodeInfo) select.evaluateItem(context);
459             } else {
460                 Item item = context.getContextItem();
461                 if (!(item instanceof NodeInfo)) {
462                     DynamicError err = new DynamicError("context item for xsl:number must be a node");
463                     err.setErrorCode("XTTE0990");
464                     err.setIsTypeError(true);
465                     err.setXPathContext(context);
466                     throw err;
467                     //recoverableError(err, context);
468
//return null; // error recovery action is to output nothing
469
}
470                 source = (NodeInfo) item;
471             }
472
473             if (level == SIMPLE) {
474                 value = Navigator.getNumberSimple(source, context);
475             } else if (level == SINGLE) {
476                 value = Navigator.getNumberSingle(source, count, from, context);
477                 if (value == 0) {
478                     vec = Collections.EMPTY_LIST; // an empty list
479
}
480             } else if (level == ANY) {
481                 value = Navigator.getNumberAny(this, source, count, from, context, hasVariablesInPatterns);
482                 if (value == 0) {
483                     vec = Collections.EMPTY_LIST; // an empty list
484
}
485             } else if (level == MULTI) {
486                 vec = Navigator.getNumberMulti(source, count, from, context);
487             }
488         }
489
490         int gpsize = 0;
491         String JavaDoc gpseparator = "";
492         String JavaDoc letterVal;
493         String JavaDoc ordinalVal = null;
494
495         if (groupSize != null) {
496             String JavaDoc g = groupSize.evaluateAsString(context);
497             try {
498                 gpsize = Integer.parseInt(g);
499             } catch (NumberFormatException JavaDoc err) {
500                 DynamicError e = new DynamicError("grouping-size must be numeric");
501                 e.setXPathContext(context);
502                 e.setErrorCode("XTDE0030");
503                 throw e;
504             }
505         }
506
507         if (groupSeparator != null) {
508             gpseparator = groupSeparator.evaluateAsString(context);
509         }
510
511         if (ordinal != null) {
512             ordinalVal = ordinal.evaluateAsString(context);
513         }
514
515         // fast path for the simple case
516

517         if (vec == null && format == null && gpsize == 0 && lang == null) {
518             return new StringValue("" + value);
519         }
520
521         // Use the numberer decided at compile time if possible; otherwise try to get it from
522
// a table of numberers indexed by language; if not there, load the relevant class and
523
// add it to the table.
524
Numberer numb = numberer;
525         if (numb == null) {
526             String JavaDoc language = lang.evaluateAsString(context);
527             if (nationalNumberers == null) {
528                 nationalNumberers = new HashMap(4);
529             }
530             numb = (Numberer)nationalNumberers.get(language);
531             if (numb == null) {
532                 numb = makeNumberer(language, context);
533                 nationalNumberers.put(language, numb);
534             }
535         }
536
537         if (letterValue == null) {
538             letterVal = "";
539         } else {
540             letterVal = letterValue.evaluateAsString(context);
541             if (!("alphabetic".equals(letterVal) || "traditional".equals(letterVal))) {
542                 DynamicError e = new DynamicError("letter-value must be \"traditional\" or \"alphabetic\"");
543                 e.setXPathContext(context);
544                 e.setErrorCode("XTDE0030");
545                 throw e;
546             }
547         }
548
549         if (vec == null) {
550             vec = new ArrayList(1);
551             vec.add(new Long JavaDoc(value));
552         }
553
554         NumberFormatter nf;
555         if (formatter == null) { // format not known until run-time
556
nf = new NumberFormatter();
557             nf.prepare(format.evaluateAsString(context));
558         } else {
559             nf = formatter;
560         }
561
562         CharSequence JavaDoc s = nf.format(vec, gpsize, gpseparator, letterVal, ordinalVal, numb);
563         return new StringValue(s);
564     }
565
566     private void recoverableError(DynamicError error, XPathContext context) throws XPathException {
567         error.setLocator(ExpressionTool.getLocator(this));
568         context.getController().recoverableError(error);
569     }
570
571     /**
572      * Load a Numberer class for a given language and check it is OK.
573      * @param language the language for which a Numberer is required
574      * @return a suitable numberer. If no specific numberer is available
575      * for the language, the default (English) numberer is used.
576      */

577
578     public static Numberer makeNumberer(String JavaDoc language, XPathContext context) {
579
580         Numberer numberer;
581         if ("en".equals(language)) {
582             numberer = defaultNumberer;
583         } else {
584             String JavaDoc langClassName = "net.sf.saxon.number.Numberer_";
585             for (int i = 0; i < language.length(); i++) {
586                 if (Character.isLetter(language.charAt(i))) {
587                     langClassName += language.charAt(i);
588                 }
589             }
590             try {
591                 Controller controller = context.getController();
592                 Configuration config = controller.getConfiguration();
593                 numberer = (Numberer) (config.getInstance(langClassName, controller.getClassLoader()));
594             } catch (XPathException err) {
595                 numberer = defaultNumberer;
596             }
597         }
598
599         return numberer;
600     }
601
602     /**
603      * Diagnostic print of expression structure. The expression is written to the System.err
604      * output stream
605      *
606      * @param level indentation level for this expression
607      * @param out
608      */

609
610     public void display(int level, NamePool pool, PrintStream JavaDoc out) {
611         out.println(ExpressionTool.indent(level) + "xsl:number");
612     }
613 }
614
615 //
616
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
617
// you may not use this file except in compliance with the License. You may obtain a copy of the
618
// License at http://www.mozilla.org/MPL/
619
//
620
// Software distributed under the License is distributed on an "AS IS" basis,
621
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
622
// See the License for the specific language governing rights and limitations under the License.
623
//
624
// The Original Code is: all this file.
625
//
626
// The Initial Developer of the Original Code is Michael H. Kay.
627
//
628
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
629
//
630
// Contributor(s): none.
631
//
632
Popular Tags