1 package net.sf.saxon.functions; 2 import net.sf.saxon.Err; 3 import net.sf.saxon.TransformerFactoryImpl; 4 import net.sf.saxon.Controller; 5 import net.sf.saxon.Configuration; 6 import net.sf.saxon.style.StandardNames; 7 import net.sf.saxon.tinytree.TinyBuilder; 8 import net.sf.saxon.charcode.UnicodeCharacterSet; 9 import net.sf.saxon.expr.*; 10 import net.sf.saxon.om.*; 11 import net.sf.saxon.sort.GlobalOrderComparer; 12 import net.sf.saxon.trace.Location; 13 import net.sf.saxon.trans.DynamicError; 14 import net.sf.saxon.trans.XPathException; 15 import net.sf.saxon.type.SchemaType; 16 import net.sf.saxon.type.Type; 17 import net.sf.saxon.value.*; 18 19 import javax.xml.transform.Templates ; 20 import javax.xml.transform.TransformerConfigurationException ; 21 import javax.xml.transform.Transformer ; 22 import javax.xml.transform.TransformerException ; 23 import java.math.BigDecimal ; 24 import java.util.ArrayList ; 25 import java.util.List ; 26 import java.io.ByteArrayInputStream ; 27 import java.io.InputStreamReader ; 28 import java.io.ByteArrayOutputStream ; 29 import java.io.OutputStreamWriter ; 30 31 40 41 42 43 public class Extensions { 44 45 private Extensions() {} 47 48 51 52 public static void pauseTracing(XPathContext c) { 53 c.getController().pauseTracing(true); 54 } 55 56 60 61 62 public static void resumeTracing(XPathContext c) { 63 c.getController().pauseTracing(false); 64 } 65 66 69 70 public static String systemId(XPathContext c) throws XPathException { 71 Item item = c.getContextItem(); 72 if (item==null) { 73 DynamicError e = new DynamicError("The context item for saxon:systemId() is not set"); 74 e.setXPathContext(c); 75 throw e; 76 } 77 if (item instanceof NodeInfo) { 78 return ((NodeInfo)item).getSystemId(); 79 } else { 80 return ""; 81 } 82 } 83 84 87 public static int lineNumber(XPathContext c) { 88 Item item = c.getCurrentIterator().current(); 89 if (item instanceof NodeInfo) { 90 return ((NodeInfo)item).getLineNumber(); 91 } else { 92 return -1; 93 } 94 } 95 96 99 public static int lineNumber(NodeInfo node) { 100 return node.getLineNumber(); 101 } 102 103 113 114 public static DocumentInfo discardDocument(XPathContext context, DocumentInfo doc) { 115 return context.getController().getDocumentPool().discard(doc); 116 } 117 118 124 125 public static boolean hasSameNodes(SequenceIterator p1, SequenceIterator p2) throws XPathException { 126 SequenceIterator e1 = p1; 127 SequenceIterator e2 = p2; 128 129 while (true) { 130 NodeInfo n1 = (NodeInfo)e1.next(); 131 NodeInfo n2 = (NodeInfo)e2.next(); 132 if (n1==null || n2==null) { 133 return n1==n2; 134 } 135 if (!n1.isSameNodeInfo(n2)) { 136 return false; 137 } 138 } 139 } 140 141 142 145 146 152 153 public static double sum (XPathContext context, 154 SequenceIterator nsv, 155 Evaluate.PreparedExpression pexpression) throws XPathException { 156 157 double total = 0.0; 158 XPathContext c = context.newMinorContext(); 159 c.setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION); 160 c.setCurrentIterator(nsv); 161 while (true) { 162 Item next = nsv.next(); 163 if (next == null) break; 164 Item val = pexpression.expression.evaluateItem(c); 165 if (val instanceof NumericValue) { 166 DoubleValue v = (DoubleValue)((NumericValue)val).convert(Type.DOUBLE, context); 167 total += v.getDoubleValue(); 168 } else { 169 DynamicError e = new DynamicError("expression in saxon:sum() must return numeric values"); 170 e.setXPathContext(c); 171 throw e; 172 } 173 } 174 return total; 175 } 176 177 180 181 public static double max (XPathContext context, 182 SequenceIterator nsv, 183 Evaluate.PreparedExpression pexpression) throws XPathException { 184 double max = Double.NEGATIVE_INFINITY; 185 XPathContext c = context.newMinorContext(); 186 c.setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION); 187 c.setCurrentIterator(nsv); 188 while (true) { 189 Item next = nsv.next(); 190 if (next==null) break; 191 Item val = pexpression.expression.evaluateItem(c); 192 if (val instanceof NumericValue) { 193 DoubleValue v = (DoubleValue)((NumericValue)val).convert(Type.DOUBLE, context); 194 if (v.getDoubleValue()>max) max = v.getDoubleValue(); 195 } else { 196 DynamicError e = new DynamicError("expression in saxon:max() must return numeric values"); 197 e.setXPathContext(c); 198 throw e; 199 } 200 } 201 return max; 202 } 203 204 207 208 public static double min (XPathContext context, 209 SequenceIterator nsv, 210 Evaluate.PreparedExpression pexpression) throws XPathException { 211 double min = Double.POSITIVE_INFINITY; 212 XPathContext c = context.newMinorContext(); 213 c.setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION); 214 c.setCurrentIterator(nsv); 215 while (true) { 216 Item next = nsv.next(); 217 if (next==null) break; 218 Item val = pexpression.expression.evaluateItem(c); 219 if (val instanceof NumericValue) { 220 DoubleValue v = (DoubleValue)((NumericValue)val).convert(Type.DOUBLE, context); 221 if (v.getDoubleValue()<min) min = v.getDoubleValue(); 222 } else { 223 DynamicError e = new DynamicError("expression in saxon:min() must return numeric values"); 224 e.setXPathContext(context); 225 throw e; 226 } 227 } 228 return min; 229 } 230 231 234 235 public static Value highest (SequenceIterator nsv) throws XPathException { 236 return net.sf.saxon.exslt.Math.highest(nsv); 237 } 238 239 240 243 244 public static SequenceIterator highest (XPathContext context, 245 SequenceIterator nsv, 246 Evaluate.PreparedExpression pexpression) throws XPathException { 247 double max = Double.NEGATIVE_INFINITY; 248 XPathContext c = context.newMinorContext(); 249 c.setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION); 250 Item highest = null; 251 c.setCurrentIterator(nsv); 252 while (true) { 253 Item next = nsv.next(); 254 if (next==null) break; 255 Item val = pexpression.expression.evaluateItem(c); 256 if (val instanceof NumericValue) { 257 DoubleValue v = (DoubleValue)((NumericValue)val).convert(Type.DOUBLE, context); 258 if (v.getDoubleValue()>max) { 259 max = v.getDoubleValue(); 260 highest = nsv.current(); 261 } 262 } else { 263 DynamicError e = new DynamicError("expression in saxon:highest() must return numeric values"); 264 e.setXPathContext(context); 265 throw e; 266 } 267 } 268 return SingletonIterator.makeIterator(highest); 269 } 270 271 274 275 public static Value lowest (SequenceIterator nsv) throws XPathException { 276 return net.sf.saxon.exslt.Math.lowest(nsv); 277 } 278 279 282 283 public static SequenceIterator lowest (XPathContext context, 284 SequenceIterator nsv, 285 Evaluate.PreparedExpression pexpression) throws XPathException { 286 double min = Double.POSITIVE_INFINITY; 287 XPathContext c = context.newMinorContext(); 288 c.setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION); 289 Item lowest = null; 290 c.setCurrentIterator(nsv); 291 while (true) { 292 Item next = nsv.next(); 293 if (next==null) break; 294 Item val = pexpression.expression.evaluateItem(c); 295 if (val instanceof NumericValue) { 296 DoubleValue v = (DoubleValue)((NumericValue)val).convert(Type.DOUBLE, context); 297 if (v.getDoubleValue()<min) { 298 min = v.getDoubleValue(); 299 lowest = nsv.current(); 300 } 301 } else { 302 DynamicError e = new DynamicError("expression in saxon:lowest() must return numeric values"); 303 e.setXPathContext(context); 304 throw e; 305 } 306 } 307 return SingletonIterator.makeIterator(lowest); 308 } 309 310 314 315 public static SequenceIterator leading (XPathContext context, 316 SequenceIterator in, Evaluate.PreparedExpression pexp) { 317 XPathContext c2 = context.newMinorContext(); 318 c2.setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION); 319 return new FilterIterator.Leading(in, pexp.expression, c2); 320 } 321 322 326 327 330 public static SequenceIterator after ( 331 XPathContext context, 332 SequenceIterator ns1, 333 SequenceIterator ns2) throws XPathException { 334 335 NodeInfo first = null; 336 337 339 GlobalOrderComparer comparer = GlobalOrderComparer.getInstance(); 340 while (true) { 341 Item item = ns2.next(); 342 if (item == null) { 343 if (first == null) { 344 return ns1; 345 } else { 346 break; 347 } 348 } 349 if (item instanceof NodeInfo) { 350 NodeInfo node = (NodeInfo)item; 351 if (first==null) { 352 first = node; 353 } else { 354 if (comparer.compare(node, first) < 0) { 355 first = node; 356 } 357 } 358 } else { 359 DynamicError e = new DynamicError("Operand of after() contains an item that is not a node"); 360 e.setXPathContext(context); 361 throw e; 362 } 363 } 364 365 367 Expression filter = new IdentityComparison( 368 new ContextItemExpression(), 369 Token.FOLLOWS, 370 new SingletonNode(first)); 371 372 return new FilterIterator(ns1, filter, context); 373 374 } 375 376 377 381 382 384 public static SequenceIterator tokenize(String s) { 385 if (s == null) { 386 return EmptyIterator.getInstance(); 388 } 389 return new StringTokenIterator(s); 390 } 391 392 397 398 400 public static SequenceIterator tokenize(String s, String delim) { 401 if (s == null) { 402 return EmptyIterator.getInstance(); 404 } 405 return new StringTokenIterator(s, delim); 406 } 407 408 409 410 413 414 public static String path(XPathContext c) throws XPathException { 415 Item item = c.getContextItem(); 416 if (item==null) { 417 DynamicError e = new DynamicError("The context item for saxon:path() is not set"); 418 e.setXPathContext(c); 419 throw e; 420 } 421 if (item instanceof NodeInfo) { 422 return Navigator.getPath((NodeInfo)item); 423 } else { 424 return ""; 425 } 426 } 427 428 431 432 public static String typeAnnotation(XPathContext context, NodeInfo node) { 433 int code = node.getTypeAnnotation(); 434 if ((code & NodeInfo.IS_DTD_TYPE) != 0) { 435 code = StandardNames.XDT_UNTYPED_ATOMIC; 436 } 437 if (code == -1) { 438 int nodeKind = node.getNodeKind(); 439 if (nodeKind == Type.ELEMENT || nodeKind == Type.DOCUMENT) { 440 return "untyped"; 441 } else { 442 return "untypedAtomic"; 443 } 444 } 445 SchemaType type = context.getController().getConfiguration().getSchemaType(code & 0xfffff); 446 if (type==null) { 447 return context.getController().getNamePool().getDisplayName(code); 449 } 450 return "type " + type.getDescription(); 451 } 452 453 456 457 public static XPathContext getContext(XPathContext c) { 458 return c; 459 } 460 461 464 465 public static Controller getController(XPathContext c) { 466 return c.getController(); 467 } 468 469 472 473 public static Configuration getConfiguration(XPathContext c) { 474 return c.getController().getConfiguration(); 475 } 476 477 482 483 public static String getPseudoAttribute(XPathContext c, String name) 484 throws XPathException { 485 Item pi = c.getContextItem(); 486 if (pi==null) { 487 DynamicError e = new DynamicError("The context item for saxon:getPseudoAttribute() is not set"); 488 e.setXPathContext(c); 489 throw e; 490 } 491 String val = ProcInstParser.getPseudoAttribute(pi.getStringValue(), name); 493 if (val==null) return ""; 494 return val; 495 } 496 497 500 public static SecondsDurationValue dayTimeDurationFromSeconds(double arg) throws XPathException { 502 return SecondsDurationValue.fromSeconds(arg); 503 } 504 505 508 public static MonthDurationValue yearMonthDurationFromMonths(double arg) { 510 return MonthDurationValue.fromMonths((int)arg); 511 } 512 513 516 517 public static BigDecimal decimalDivide(BigDecimal arg1, BigDecimal arg2, int scale) { 518 return arg1.divide(arg2, scale, BigDecimal.ROUND_DOWN); 519 } 520 521 522 528 529 public static List stringToUtf8(String in) { 530 ArrayList list = new ArrayList (in.length()*2); 531 byte[] octets = new byte[4]; 532 for (int i=0; i<in.length(); i++) { 533 int used = UnicodeCharacterSet.getUTF8Encoding( 534 in.charAt(i), (i+1<in.length()? in.charAt(i+1): (char)0), octets); 535 for (int j=0; j<used; j++) { 536 list.add(new Integer (255&(int)octets[j])); 537 } 538 } 539 return list; 540 } 541 542 546 547 public static Base64BinaryValue octetsToBase64Binary(byte[] in) { 548 return new Base64BinaryValue(in); 549 } 550 551 555 556 public static HexBinaryValue octetsToHexBinary(byte[] in) { 557 return new HexBinaryValue(in); 558 } 559 560 563 564 public static byte[] base64BinaryToOctets(Base64BinaryValue in) { 565 return in.getBinaryValue(); 566 } 567 568 571 572 public static byte[] hexBinaryToOctets(HexBinaryValue in) { 573 return in.getBinaryValue(); 574 } 575 576 579 580 public static String base64BinaryToString(Base64BinaryValue in, String encoding) throws Exception { 581 byte[] bytes = in.getBinaryValue(); 582 ByteArrayInputStream stream = new ByteArrayInputStream (bytes); 583 InputStreamReader reader = new InputStreamReader (stream, encoding); 584 char[] array = new char[bytes.length]; 585 int used = reader.read(array, 0, array.length); 586 checkBytes(array, 0, used); 587 return new String (array, 0, used); 588 } 589 590 593 594 public static Base64BinaryValue stringToBase64Binary(String in, String encoding) throws Exception { 595 ByteArrayOutputStream stream = new ByteArrayOutputStream (in.length()); 596 OutputStreamWriter writer = new OutputStreamWriter (stream, encoding); 597 writer.write(in); 598 writer.close(); 599 byte[] bytes = stream.toByteArray(); 600 return octetsToBase64Binary(bytes); 601 } 602 603 606 607 public static String hexBinaryToString(HexBinaryValue in, String encoding) throws Exception { 608 byte[] bytes = in.getBinaryValue(); 609 ByteArrayInputStream stream = new ByteArrayInputStream (bytes); 610 InputStreamReader reader = new InputStreamReader (stream, encoding); 611 char[] array = new char[bytes.length]; 612 int used = reader.read(array, 0, array.length); 613 checkBytes(array, 0, used); 614 return new String (array, 0, used); 615 } 616 617 620 621 private static void checkBytes(char[] array, int start, int end) throws XPathException { 622 for (int c=start; c<end; c++) { 623 int ch32 = array[c]; 624 if (XMLChar.isHighSurrogate(ch32)) { 625 char low = array[c++]; 626 ch32 = XMLChar.supplemental((char)ch32, low); 627 } 628 if (!XMLChar.isValid(ch32)) { 629 DynamicError err = new DynamicError( 630 "The byte sequence contains a character not allowed by XML (hex " + 631 Integer.toHexString(ch32) + ')'); 632 err.setErrorCode("XTDE1180"); 633 throw err; 634 } 635 } 636 } 637 638 641 642 public static HexBinaryValue stringToHexBinary(String in, String encoding) throws Exception { 643 ByteArrayOutputStream stream = new ByteArrayOutputStream (in.length()); 644 OutputStreamWriter writer = new OutputStreamWriter (stream, encoding); 645 writer.write(in); 646 writer.close(); 647 byte[] bytes = stream.toByteArray(); 648 return octetsToHexBinary(bytes); 649 } 650 651 654 655 public static boolean validCharacter(int in) { 656 return XMLChar.isValid(in); 657 } 658 659 660 664 665 public static NodeInfo namespaceNode(XPathContext context, String prefix, String uri) throws XPathException { 666 if (prefix == null) { 667 prefix = ""; 668 } 669 if (!("".equals(prefix) || XMLChar.isValidNCName(prefix))) { 670 DynamicError err = new DynamicError("Namespace prefix " + Err.wrap(prefix) + " is not a valid NCName"); 671 throw err; 672 } 673 if (uri==null || "".equals(uri)) { 674 DynamicError err = new DynamicError("URI of namespace node must not be empty"); 675 throw err; 676 } 677 final NamePool namePool = context.getController().getNamePool(); 678 Orphan node = new Orphan(context.getController().getConfiguration()); 679 node.setNodeKind(Type.NAMESPACE); 680 node.setNameCode(namePool.allocate("", "", prefix)); 681 node.setStringValue(uri); 682 return node; 683 } 684 685 689 690 public static Templates compileStylesheet(XPathContext context, DocumentInfo doc) throws XPathException { 691 try { 692 TransformerFactoryImpl factory = new TransformerFactoryImpl(context.getController().getConfiguration()); 693 return factory.newTemplates(doc); 694 } catch (TransformerConfigurationException e) { 695 throw DynamicError.makeDynamicError(e); 696 } 697 } 698 699 705 706 public static DocumentInfo transform(XPathContext context, Templates templates, NodeInfo source) throws XPathException { 707 try { 708 Transformer transformer = templates.newTransformer(); 709 TinyBuilder builder = new TinyBuilder(); 710 builder.setPipelineConfiguration(context.getController().makePipelineConfiguration()); 711 transformer.transform(source, builder); 712 return (DocumentInfo)builder.getCurrentRoot(); 713 } catch (TransformerException e) { 714 throw DynamicError.makeDynamicError(e); 715 } 716 } 717 } 718 719 720 721 722 723 | Popular Tags |